[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "### 运行环境 ###\n\n- [x] 设备型号：如：`Nexus 6`\n- [x] 设备系统版本：如 `Android 5.0`\n- [x] Gradle 版本：如 `2.3.0`\n- [x] QMUI Android 版本：`1.x.x`\n\n### 具体问题描述 ###\n\n#### 问题截图 ####\n\n#### Layout Inspector 文件([如何获取](https://github.com/QMUI/QMUI_Android/wiki/%E6%8F%90%E4%BE%9B-Layout-Inspector-%E6%96%87%E4%BB%B6)) ####\n\n#### 异常日志（堆栈） ####\n"
  },
  {
    "path": ".gitignore",
    "content": "/*.bin\n*.iml\n.DS_Store\n/.gradle\n/.gradletasknamecache\n/.idea\n/bin\n/build\n/local.properties\n/captures\n/gradle/deploy.properties\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "[腾讯开源激励计划](https://opensource.tencent.com/contribution) 鼓励开发者的参与和贡献，期待你的加入。我们欢迎[report Issues](https://github.com/Tencent/QMUI_Android/issues) 或者 [pull requests](https://github.com/Tencent/QMUI_Android/pulls)。 在贡献代码之前请阅读以下指引。\n\n## 问题管理\n我们用 Github Issues 去跟踪 public bugs 和 feature requests。\n\n### 使用 Issues\n\n1. 新建 issues 前，请查找已存在或者相类似的 issue，从而保证不存在冗余。\n2. 新建 issues 时，请根据我们提供的 issue 模板，尽可能提供详细的描述、截屏或者短视频来辅助我们定位问题。\n\n###  Pull Requests\n\n我们欢迎大家为 QMUI_Android 贡献代码，在完成一个 pull request 之前请确认:\n\n1. 从 `master` fork 你自己的分支。\n2. 在修改了代码之后请修改对应的文档和注释。\n3. 在新建的文件中请加入 licence 和 copy right 声明。\n4. 确保一致的代码风格。\n5. 做充分的测试。\n"
  },
  {
    "path": "LICENSE.TXT",
    "content": "Tencent is pleased to support the open source community by making QMUI_Android available.  \nCopyright (C) 2017-2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the QMUI_Android binary from Tencent, please note that the QMUI_Android binary is licensed under the MIT License.\nIf you have downloaded a copy of the QMUI_Android source code from Tencent, please note that QMUI_Android source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms.  Your integration of QMUI_Android into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within QMUI_Android.\nA copy of the MIT License is included in this file.\n\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \n----------------------------------------------------------------------------------------\n1. JavaPoet  1.7.0\nCopyright 2015 Square, Inc.\n\n2. LeakCanary  1.5.4\nCopyright 2015 Square, Inc.\n\n3. Butterknife  8.8.1\nCopyright 2013 Jake Wharton\n\n\nTerms of the Apache License, Version 2.0:\n---------------------------------------------------\nApache License\n\nVersion 2.0, January 2004\n\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\nLicense shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\nLicensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\nLegal Entity shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, control means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\nYou (or Your) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\nSource form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\nObject form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\nWork shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\nDerivative Works shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\nContribution shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, submitted means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as Not a Contribution.\n\nContributor shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a NOTICE text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nTerms of the MIT License:\n--------------------------------------------------------------------\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": " <p align=\"center\">\n  <img src=\"https://cloud.githubusercontent.com/assets/1190261/26751376/63f96538-486a-11e7-81cf-5bc83a945207.png\" width=\"220\" height=\"220\" alt=\"Banner\" />\n</p>\n\n# QMUI_Android\n\nQMUI Android 的设计目的是用于辅助快速搭建一个具备基本设计还原效果的 Android 项目，同时利用自身提供的丰富控件及兼容处理，让开发者能专注于业务需求而无需耗费精力在基础代码的设计上。不管是新项目的创建，或是已有项目的维护，均可使开发效率和项目质量得到大幅度提升。\n\n[![QMUI Team Name](https://img.shields.io/badge/Team-QMUI-brightgreen.svg?style=flat)](https://github.com/QMUI \"QMUI Team\")\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT \"Feel free to contribute.\")\n\n## 功能特性\n### 全局 UI 配置\n只需要修改一份配置表就可以调整 App 的全局样式，包括组件颜色、导航栏、对话框、列表等。一处修改，全局生效。\n\n### 丰富的 UI 控件\n提供丰富常用的 UI 控件，例如 BottomSheet、Tab、圆角 ImageView、下拉刷新等，使用方便灵活，并且支持自定义控件的样式。\n\n### 高效的工具方法\n提供高效的工具方法，包括设备信息、屏幕信息、键盘管理、状态栏管理等，可以解决各种常见场景并大幅度提升开发效率。\n\n## 支持 Android 版本\nQMUI Android 支持 API Level 21+。\n\n## 使用方法\n可以在工程中的 qmuidemo 项目中查看各组件的使用。\n\n## 隐私与安全\n1. 框架会调用 android.os.Build 下的字段读取 brand、model 等信息，用于区分不同的设备。\n2. 框架会尝试读取系统设置获取是否是全面屏手势\n"
  },
  {
    "path": "arch/.gitignore",
    "content": "/*.bin\n/*.iml\n/.DS_Store\n/.gradletasknamecache\n/.idea\n/bin\n/build\n/local.properties\n/deploy.properties"
  },
  {
    "path": "arch/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.archVer\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n}\n\ndependencies {\n    api(Dep.AndroidX.appcompat)\n    api(Dep.AndroidX.fragment)\n    api(project(\":arch-annotation\"))\n    compileOnly(project(\":qmui\"))\n}"
  },
  {
    "path": "arch/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "arch/src/androidTest/java/com/qmuiteam/qmui/arch/ExampleInstrumentedTest.java",
    "content": "package com.qmuiteam.qmui.arch;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.qmuiteam.qmui.arch.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "arch/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.qmuiteam.qmui.arch\"/>\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/InnerBaseActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.annotation.SuppressLint;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\n\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.view.LayoutInflaterCompat;\nimport androidx.lifecycle.Lifecycle;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.arch.record.LatestVisitArgumentCollector;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.arch.scheme.QMUISchemeHandler;\nimport com.qmuiteam.qmui.skin.QMUISkinLayoutInflaterFactory;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n\n//Fix the bug: Only fullscreen activities can request orientation in Android version 26, 27\nclass InnerBaseActivity extends AppCompatActivity implements LatestVisitArgumentCollector {\n    private static int NO_REQUESTED_ORIENTATION_SET = -100;\n    private static final AtomicInteger sNextRc = new AtomicInteger(1);\n    private static int sLatestVisitActivityUUid = -1;\n    private boolean mConvertToTranslucentCauseOrientationChanged = false;\n    private int mPendingRequestedOrientation = NO_REQUESTED_ORIENTATION_SET;\n    private QMUISkinManager mSkinManager;\n    private final int mUUid = sNextRc.getAndIncrement();\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        if (useQMUISkinLayoutInflaterFactory()) {\n            LayoutInflater layoutInflater = LayoutInflater.from(this);\n            LayoutInflaterCompat.setFactory2(layoutInflater,\n                    new QMUISkinLayoutInflaterFactory(this, layoutInflater));\n        }\n        super.onCreate(savedInstanceState);\n    }\n\n    void convertToTranslucentCauseOrientationChanged() {\n        Utils.convertActivityToTranslucent(this);\n        mConvertToTranslucentCauseOrientationChanged = true;\n    }\n\n    @Override\n    public void setRequestedOrientation(int requestedOrientation) {\n        if (mConvertToTranslucentCauseOrientationChanged && (Build.VERSION.SDK_INT == Build.VERSION_CODES.O\n                || Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1)) {\n            Log.i(\"InnerBaseActivity\", \"setRequestedOrientation when activity is translucent\");\n            mPendingRequestedOrientation = requestedOrientation;\n        } else {\n            super.setRequestedOrientation(requestedOrientation);\n        }\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        if (mConvertToTranslucentCauseOrientationChanged) {\n            mConvertToTranslucentCauseOrientationChanged = false;\n            Utils.convertActivityFromTranslucent(this);\n            if (mPendingRequestedOrientation != NO_REQUESTED_ORIENTATION_SET) {\n                super.setRequestedOrientation(mPendingRequestedOrientation);\n                mPendingRequestedOrientation = NO_REQUESTED_ORIENTATION_SET;\n            }\n        }\n    }\n\n    public QMUISkinManager getSkinManager() {\n        return mSkinManager;\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        if (mSkinManager != null) {\n            mSkinManager.register(this);\n        }\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        if (mSkinManager != null) {\n            mSkinManager.unRegister(this);\n        }\n    }\n\n    @Override\n    protected void onResume() {\n        checkLatestVisitRecord();\n        super.onResume();\n    }\n\n    public final void onLatestVisitArgumentChanged() {\n        if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.INITIALIZED)\n                && sLatestVisitActivityUUid == mUUid) {\n            checkLatestVisitRecord();\n        }\n    }\n\n    protected boolean shouldPerformLatestVisitRecord() {\n        return true;\n    }\n\n    private void checkLatestVisitRecord() {\n        Class<? extends InnerBaseActivity> cls = getClass();\n        sLatestVisitActivityUUid = mUUid;\n\n        if (!shouldPerformLatestVisitRecord()) {\n            QMUILatestVisit.getInstance(this).clearActivityLatestVisitRecord();\n            return;\n        }\n\n        LatestVisitRecord latestVisitRecord = cls.getAnnotation(LatestVisitRecord.class);\n        if(latestVisitRecord == null || (latestVisitRecord.onlyForDebug() && !QMUIConfig.DEBUG)){\n            QMUILatestVisit.getInstance(this).clearActivityLatestVisitRecord();\n            return;\n        }\n        QMUILatestVisit.getInstance(this).performLatestVisitRecord(this);\n    }\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n\n    }\n\n    public void setSkinManager(@Nullable QMUISkinManager skinManager) {\n        if (mSkinManager != null) {\n            mSkinManager.unRegister(this);\n        }\n        mSkinManager = skinManager;\n        if (skinManager != null) {\n            if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {\n                skinManager.register(this);\n            }\n        }\n    }\n\n    public final boolean isStartedByScheme() {\n        return getIntent().getBooleanExtra(QMUISchemeHandler.ARG_FROM_SCHEME, false);\n    }\n\n    protected boolean useQMUISkinLayoutInflaterFactory() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUIActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.arch.scheme.ActivitySchemeRefreshable;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIStatusBarHelper;\n\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_BOTTOM_TO_TOP;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_LEFT_TO_RIGHT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_NONE;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_RIGHT_TO_LEFT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_TOP_TO_BOTTOM;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_BOTTOM;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_LEFT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_RIGHT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_TOP;\n\npublic class QMUIActivity extends InnerBaseActivity implements ActivitySchemeRefreshable {\n    private static final String TAG = \"QMUIActivity\";\n    private SwipeBackLayout.ListenerRemover mListenerRemover;\n    private SwipeBackgroundView mSwipeBackgroundView;\n    private boolean mIsInSwipeBack = false;\n\n    private SwipeBackLayout.SwipeListener mSwipeListener = new SwipeBackLayout.SwipeListener() {\n\n        @Override\n        public void onScrollStateChange(int state, float scrollPercent) {\n            Log.i(TAG, \"SwipeListener:onScrollStateChange: state = \" + state + \" ;scrollPercent = \" + scrollPercent);\n            mIsInSwipeBack = state != SwipeBackLayout.STATE_IDLE;\n            if (state == SwipeBackLayout.STATE_IDLE) {\n                if (mSwipeBackgroundView != null) {\n                    if (scrollPercent <= 0.0F) {\n                        mSwipeBackgroundView.unBind();\n                        mSwipeBackgroundView = null;\n                    } else if (scrollPercent >= 1.0F) {\n                        // unBind mSwipeBackgroundView until onDestroy\n                        finish();\n                        int exitAnim = mSwipeBackgroundView.hasChildWindow() ?\n                                R.anim.swipe_back_exit_still : R.anim.swipe_back_exit;\n                        overridePendingTransition(R.anim.swipe_back_enter, exitAnim);\n                    }\n                }\n            }\n        }\n\n        @Override\n        public void onScroll(int dragDirection, int movingEdge, float scrollPercent) {\n            if (mSwipeBackgroundView != null) {\n                scrollPercent = Math.max(0f, Math.min(1f, scrollPercent));\n                int targetOffset = (int) (Math.abs(backViewInitOffset(\n                        QMUIActivity.this, dragDirection, movingEdge)) * (1 - scrollPercent));\n                SwipeBackLayout.translateInSwipeBack(mSwipeBackgroundView, movingEdge, targetOffset);\n            }\n        }\n\n        @Override\n        public void onSwipeBackBegin(int dragDirection, int moveEdge) {\n            Log.i(TAG, \"SwipeListener:onSwipeBackBegin: moveEdge = \" + moveEdge);\n            onDragStart();\n            ViewGroup decorView = (ViewGroup) getWindow().getDecorView();\n            if (decorView != null) {\n                Activity prevActivity = QMUISwipeBackActivityManager.getInstance()\n                        .getPenultimateActivity(QMUIActivity.this);\n                if(prevActivity == null){\n                    return;\n                }\n                if (decorView.getChildAt(0) instanceof SwipeBackgroundView) {\n                    mSwipeBackgroundView = (SwipeBackgroundView) decorView.getChildAt(0);\n                } else {\n                    mSwipeBackgroundView = new SwipeBackgroundView(QMUIActivity.this, forceDisableHardwareAcceleratedForSwipeBackground());\n                    decorView.addView(mSwipeBackgroundView, 0, new FrameLayout.LayoutParams(\n                            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n                }\n                mSwipeBackgroundView.bind(prevActivity,\n                        QMUIActivity.this, restoreSubWindowWhenDragBack());\n                SwipeBackLayout.translateInSwipeBack(mSwipeBackgroundView, moveEdge,\n                        Math.abs(backViewInitOffset(decorView.getContext(), dragDirection, moveEdge)));\n            }\n        }\n\n        @Override\n        public void onScrollOverThreshold() {\n            Log.i(TAG, \"SwipeListener:onEdgeTouch:onScrollOverThreshold\");\n        }\n    };\n    private SwipeBackLayout.Callback mSwipeCallback = new SwipeBackLayout.Callback() {\n\n        @Override\n        public int getDragDirection(SwipeBackLayout swipeBackLayout,\n                                    SwipeBackLayout.ViewMoveAction moveAction,\n                                    float downX, float downY, float dx, float dy, float touchSlop) {\n            if(!QMUISwipeBackActivityManager.getInstance().canSwipeBack(QMUIActivity.this)){\n                return SwipeBackLayout.DRAG_DIRECTION_NONE;\n            }\n\n            if(getIntent().getIntExtra(QMUIFragmentActivity.QMUI_MUTI_START_INDEX, 0) > 0){\n                return SwipeBackLayout.DRAG_DIRECTION_NONE;\n            }\n\n            return QMUIActivity.this.getDragDirection(swipeBackLayout,moveAction,downX, downY, dx, dy, touchSlop);\n        }\n\n        @Override\n        public void reportFrequentlyRequestLayout(int count, long duration) {\n            QMUIActivity.this.reportFrequentlyRequestLayout(count, duration);\n        }\n    };\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        performTranslucent();\n    }\n\n    protected void performTranslucent(){\n        QMUIStatusBarHelper.translucent(this);\n    }\n\n    @Override\n    public void setContentView(View view) {\n        super.setContentView(newSwipeBackLayout(view));\n    }\n\n    @Override\n    public void setContentView(int layoutResID) {\n        SwipeBackLayout swipeBackLayout = SwipeBackLayout.wrap(this, layoutResID, dragViewMoveAction(), mSwipeCallback);\n        swipeBackLayout.setOnInsetsHandler(new SwipeBackLayout.OnInsetsHandler() {\n            @Override\n            public int getInsetsType() {\n                return getRootViewInsetsType();\n            }\n        });\n        mListenerRemover = swipeBackLayout.addSwipeListener(mSwipeListener);\n        super.setContentView(swipeBackLayout);\n    }\n\n    @Override\n    public void setContentView(View view, ViewGroup.LayoutParams params) {\n        super.setContentView(newSwipeBackLayout(view), params);\n    }\n\n    private View newSwipeBackLayout(View view) {\n        final SwipeBackLayout swipeBackLayout = SwipeBackLayout.wrap(view, dragViewMoveAction(), mSwipeCallback);\n        swipeBackLayout.setOnInsetsHandler(new SwipeBackLayout.OnInsetsHandler() {\n            @Override\n            public int getInsetsType() {\n                return getRootViewInsetsType();\n            }\n        });\n        mListenerRemover = swipeBackLayout.addSwipeListener(mSwipeListener);\n        return swipeBackLayout;\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        if (mListenerRemover != null) {\n            mListenerRemover.remove();\n        }\n        if (mSwipeBackgroundView != null) {\n            mSwipeBackgroundView.unBind();\n            mSwipeBackgroundView = null;\n        }\n    }\n\n    /**\n     * final this method, if need override this method, use doOnBackPressed as an alternative\n     */\n    @Override\n    public final void onBackPressed() {\n        if (!mIsInSwipeBack) {\n            doOnBackPressed();\n        }\n    }\n\n    protected void doOnBackPressed() {\n        super.onBackPressed();\n    }\n\n    protected void reportFrequentlyRequestLayout(int count, long duration){\n        QMUILog.w(TAG, \"requestLayout is too frequent(requestLayout \" + count + \"times within \" + duration + \"ms\");\n    }\n\n    public boolean isInSwipeBack() {\n        return mIsInSwipeBack;\n    }\n\n    protected boolean forceDisableHardwareAcceleratedForSwipeBackground(){\n        return false;\n    }\n\n    /**\n     * disable or enable drag back\n     *\n     * @return if true open dragBack, otherwise close dragBack\n     * @deprecated Use {@link #getDragDirection(SwipeBackLayout, SwipeBackLayout.ViewMoveAction, float, float, float, float, float)}\n     */\n    @Deprecated\n    protected boolean canDragBack() {\n        return true;\n    }\n\n\n    /**\n     * disable or enable drag back\n     *\n     * @return if true open dragBack, otherwise close dragBack\n     * @deprecated Use {@link #getDragDirection(SwipeBackLayout, SwipeBackLayout.ViewMoveAction, float, float, float, float, float)}\n     */\n    @Deprecated\n    protected boolean canDragBack(Context context, int dragDirection, int moveEdge) {\n        return canDragBack();\n    }\n\n    /**\n     * @return the init offset for backView for Parallax scrolling\n     * @deprecated Use {@link #backViewInitOffset(Context, int, int)}\n     */\n    @Deprecated\n    protected int backViewInitOffset() {\n        return 0;\n    }\n\n    protected int backViewInitOffset(Context context, int dragDirection, int moveEdge) {\n        return backViewInitOffset();\n    }\n\n    protected int getDragDirection(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull SwipeBackLayout.ViewMoveAction viewMoveAction,\n                                   float downX, float downY, float dx, float dy, float slopTouch){\n        int targetDirection = dragBackDirection();\n        if(!canDragBack(swipeBackLayout.getContext(), targetDirection, viewMoveAction.getEdge(targetDirection))){\n            return DRAG_DIRECTION_NONE;\n        }\n        int edgeSize = QMUIDisplayHelper.dp2px(swipeBackLayout.getContext(), 20);\n        if (targetDirection == DRAG_DIRECTION_LEFT_TO_RIGHT) {\n            if(downX < edgeSize && dx >= slopTouch){\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_RIGHT_TO_LEFT) {\n            if(downX > swipeBackLayout.getWidth() - edgeSize && -dx >= slopTouch){\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_TOP_TO_BOTTOM) {\n            if(downY < edgeSize && dy >= slopTouch){\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_BOTTOM_TO_TOP) {\n            if(downY > swipeBackLayout.getHeight() - edgeSize && -dy >= slopTouch){\n                return targetDirection;\n            }\n        }\n\n        return DRAG_DIRECTION_NONE;\n    }\n\n    /**\n     * called when drag back started.\n     */\n    protected void onDragStart() {\n\n    }\n\n\n    /**\n     * @return\n     * @deprecated Use {@link #dragBackDirection()}\n     */\n    @Deprecated\n    protected int dragBackEdge() {\n        return EDGE_LEFT;\n    }\n\n    protected int dragBackDirection() {\n        int oldEdge = dragBackEdge();\n        if (oldEdge == EDGE_RIGHT) {\n            return SwipeBackLayout.DRAG_DIRECTION_RIGHT_TO_LEFT;\n        } else if (oldEdge == EDGE_TOP) {\n            return SwipeBackLayout.DRAG_DIRECTION_TOP_TO_BOTTOM;\n        } else if (oldEdge == EDGE_BOTTOM) {\n            return SwipeBackLayout.DRAG_DIRECTION_BOTTOM_TO_TOP;\n        }\n        return SwipeBackLayout.DRAG_DIRECTION_LEFT_TO_RIGHT;\n    }\n\n    protected SwipeBackLayout.ViewMoveAction dragViewMoveAction() {\n        return SwipeBackLayout.MOVE_VIEW_AUTO;\n    }\n\n    /**\n     * restore sub window(e.g dialog) when drag back to previous activity\n     *\n     * @return\n     */\n    protected boolean restoreSubWindowWhenDragBack() {\n        return true;\n    }\n\n    /**\n     * When finishing last activity, let activity have a chance to start a new Activity\n     *\n     * @return Intent to start a new Activity\n     */\n\n    public Intent onLastActivityFinish() {\n        return null;\n    }\n\n    @WindowInsetsCompat.Type.InsetsType\n    public int getRootViewInsetsType() {\n        return WindowInsetsCompat.Type.ime();\n    }\n\n    @Override\n    public void finish() {\n        if (isTaskRoot()) {\n            Intent intent = onLastActivityFinish();\n            if (intent != null) {\n                startActivity(intent);\n            }\n        }\n        super.finish();\n    }\n\n    @Override\n    public void refreshFromScheme(@Nullable Intent intent) {\n\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_BOTTOM_TO_TOP;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_LEFT_TO_RIGHT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_NONE;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_RIGHT_TO_LEFT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.DRAG_DIRECTION_TOP_TO_BOTTOM;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_BOTTOM;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_LEFT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_RIGHT;\nimport static com.qmuiteam.qmui.arch.SwipeBackLayout.EDGE_TOP;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorInflater;\nimport android.animation.AnimatorListenerAdapter;\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.widget.FrameLayout;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.arch.core.util.Function;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentContainerView;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.lifecycle.Lifecycle;\nimport androidx.lifecycle.LifecycleOwner;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MediatorLiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.lifecycle.ViewModelStoreOwner;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.arch.effect.Effect;\nimport com.qmuiteam.qmui.arch.effect.FragmentResultEffect;\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentEffectHandler;\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentEffectRegistration;\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentEffectRegistry;\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentResultEffectHandler;\nimport com.qmuiteam.qmui.arch.record.LatestVisitArgumentCollector;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.arch.scheme.FragmentSchemeRefreshable;\nimport com.qmuiteam.qmui.arch.scheme.QMUISchemeHandler;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIKeyboardHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * With the use of {@link QMUIFragmentActivity}, {@link QMUIFragment} brings more features,\n * such as swipe back, transition config, and so on.\n * <p>\n * Created by cgspine on 15/9/14.\n */\npublic abstract class QMUIFragment extends Fragment implements\n        LatestVisitArgumentCollector,\n        FragmentSchemeRefreshable{\n    static final String SWIPE_BACK_VIEW = \"swipe_back_view\";\n    private static final String TAG = QMUIFragment.class.getSimpleName();\n    private static final String QMUI_DISABLE_SWIPE_BACK_KEY = \"qmui_disable_swipe_back\";\n\n    public static final TransitionConfig SLIDE_TRANSITION_CONFIG = new TransitionConfig(\n            R.animator.slide_in_right, R.animator.slide_out_left,\n            R.animator.slide_in_left, R.animator.slide_out_right,\n            R.anim.slide_in_left, R.anim.slide_out_right\n    );\n\n    public static final TransitionConfig SCALE_TRANSITION_CONFIG = new TransitionConfig(\n            R.animator.scale_enter, R.animator.slide_still,\n            R.animator.slide_still, R.animator.scale_exit,\n            R.anim.slide_still, R.anim.scale_exit\n    );\n\n\n    public static final int RESULT_CANCELED = Activity.RESULT_CANCELED;\n    public static final int RESULT_OK = Activity.RESULT_OK;\n    public static final int RESULT_FIRST_USER = Activity.RESULT_FIRST_USER;\n\n    public static final int ANIMATION_ENTER_STATUS_NOT_START = -1;\n    public static final int ANIMATION_ENTER_STATUS_STARTED = 0;\n    public static final int ANIMATION_ENTER_STATUS_END = 1;\n    private static boolean sPopBackWhenSwipeFinished = false;\n\n    private static final int NO_REQUEST_CODE = 0;\n    private static final AtomicInteger sNextRc = new AtomicInteger(1);\n    private static int sLatestVisitFragmentUUid = -1;\n    private int mSourceRequestCode = NO_REQUEST_CODE;\n    private final int mUUid = sNextRc.getAndIncrement();\n    private int mTargetFragmentUUid = -1;\n    private int mTargetRequestCode = NO_REQUEST_CODE;\n\n    private View mBaseView;\n    private View mCacheRootView;\n    private SwipeBackLayout mCacheSwipeBackView;\n    private boolean isCreateForSwipeBack = false;\n    private SwipeBackLayout.ListenerRemover mListenerRemover;\n    private SwipeBackgroundView mSwipeBackgroundView;\n    private boolean mIsInSwipeBack = false;\n\n    private boolean mFinishActivityIfOnBackPressed = false;\n    boolean mDisableSwipeBackByMutiStarted = false;\n\n    private int mEnterAnimationStatus = ANIMATION_ENTER_STATUS_NOT_START;\n    private MutableLiveData<Boolean> isInEnterAnimationLiveData = new MutableLiveData<>(false);\n    private boolean mCalled = true;\n    private ArrayList<Runnable> mDelayRenderRunnableList;\n    private ArrayList<Runnable> mPostResumeRunnableList;\n    private Runnable mCheckPostResumeRunnable = new Runnable() {\n        @Override\n        public void run() {\n            if (isResumed() && mPostResumeRunnableList != null) {\n                ArrayList<Runnable> list = mPostResumeRunnableList;\n                if (!list.isEmpty()) {\n                    for (Runnable runnable : list) {\n                        runnable.run();\n                    }\n                }\n                mPostResumeRunnableList = null;\n            }\n        }\n    };\n    private QMUIFragmentEffectRegistry mFragmentEffectRegistry;\n\n    private OnBackPressedDispatcher mOnBackPressedDispatcher;\n    private OnBackPressedCallback mOnBackPressedCallback = new OnBackPressedCallback(true) {\n        @Override\n        public void handleOnBackPressed() {\n            if (sPopBackWhenSwipeFinished) {\n                // must use normal back procedure when swipe finished.\n                onNormalBackPressed();\n                return;\n            }\n            QMUIFragment.this.onBackPressed();\n        }\n    };\n\n    public QMUIFragment() {\n        super();\n    }\n\n    @Override\n    public void onAttach(@NonNull Context context) {\n        super.onAttach(context);\n        mOnBackPressedDispatcher = requireActivity().getOnBackPressedDispatcher();\n        mOnBackPressedDispatcher.addCallback(this, mOnBackPressedCallback);\n        registerEffect(this, new QMUIFragmentResultEffectHandler() {\n            @Override\n            public boolean shouldHandleEffect(@NonNull FragmentResultEffect effect) {\n                return effect.getRequestCode() == mSourceRequestCode && effect.getRequestFragmentUUid() == mUUid;\n            }\n\n            @Override\n            public void handleEffect(@NonNull FragmentResultEffect effect) {\n                onFragmentResult(effect.getRequestCode(), effect.getResultCode(), effect.getIntent());\n                mSourceRequestCode = NO_REQUEST_CODE;\n            }\n\n            @Override\n            public void handleEffect(@NonNull List<FragmentResultEffect> effects) {\n                // only handle the latest\n                handleEffect(effects.get(effects.size() - 1));\n            }\n        });\n    }\n\n    public final QMUIFragmentActivity getBaseFragmentActivity() {\n        return (QMUIFragmentActivity) getActivity();\n    }\n\n    public boolean isAttachedToActivity() {\n        return !isRemoving() && mBaseView != null;\n    }\n\n    @Override\n    public void onSaveInstanceState(@NonNull Bundle outState) {\n        super.onSaveInstanceState(outState);\n        outState.putBoolean(QMUI_DISABLE_SWIPE_BACK_KEY, mDisableSwipeBackByMutiStarted);\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        if (mListenerRemover != null) {\n            mListenerRemover.remove();\n            mListenerRemover = null;\n        }\n        if(getParentFragment() == null && mCacheRootView != null && mCacheRootView.getParent() instanceof ViewGroup){\n            ((ViewGroup) mCacheRootView.getParent()).removeView(mCacheRootView);\n        }\n        mBaseView = null;\n        mEnterAnimationStatus = ANIMATION_ENTER_STATUS_NOT_START;\n    }\n\n    @Override\n    public void onResume() {\n        if(mEnterAnimationStatus != ANIMATION_ENTER_STATUS_END){\n            mEnterAnimationStatus = ANIMATION_ENTER_STATUS_END;\n            notifyDelayRenderRunnableList();\n        }\n        checkLatestVisitRecord();\n        checkForRequestForHandlePopBack();\n        super.onResume();\n        if (mBaseView != null && mPostResumeRunnableList != null && !mPostResumeRunnableList.isEmpty()) {\n            mBaseView.post(mCheckPostResumeRunnable);\n        }\n    }\n\n    protected void checkForRequestForHandlePopBack(){\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n        if(provider != null){\n            provider.requestForHandlePopBack(false);\n        }\n    }\n\n    protected boolean shouldCheckLatestVisitRecord(){\n        return getParentFragment() == null || (getParentFragment() instanceof QMUINavFragment);\n    }\n\n    protected boolean shouldPerformLatestVisitRecord() {\n        return true;\n    }\n\n    private void checkLatestVisitRecord() {\n        if(!shouldCheckLatestVisitRecord()){\n            return;\n        }\n\n        Activity activity = getActivity();\n        if (!(activity instanceof QMUIFragmentActivity)) {\n            return;\n        }\n\n        if (this instanceof QMUINavFragment) {\n            return;\n        }\n\n        sLatestVisitFragmentUUid = mUUid;\n\n        if (!shouldPerformLatestVisitRecord()) {\n            QMUILatestVisit.getInstance(getContext()).clearFragmentLatestVisitRecord();\n            return;\n        }\n\n        Class<? extends QMUIFragment> cls = getClass();\n        LatestVisitRecord latestVisitRecord = cls.getAnnotation(LatestVisitRecord.class);\n        if (latestVisitRecord == null || (latestVisitRecord.onlyForDebug() && !QMUIConfig.DEBUG)) {\n            QMUILatestVisit.getInstance(getContext()).clearFragmentLatestVisitRecord();\n            return;\n        }\n\n\n        if (!activity.getClass().isAnnotationPresent(LatestVisitRecord.class)) {\n            throw new RuntimeException(String.format(\"Can not perform LatestVisitRecord, \" +\n                    \"%s must be annotated by LatestVisitRecord\", activity.getClass().getSimpleName()));\n        }\n        QMUILatestVisit.getInstance(getContext()).performLatestVisitRecord(this);\n    }\n\n    public final void onLatestVisitArgumentChanged() {\n        if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.INITIALIZED) && sLatestVisitFragmentUUid == mUUid) {\n            checkLatestVisitRecord();\n        }\n    }\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n\n    }\n\n\n    @Nullable\n    public <T extends Effect> QMUIFragmentEffectRegistration registerEffect(\n            @NonNull final LifecycleOwner lifecycleOwner,\n            @NonNull final QMUIFragmentEffectHandler<T> effectHandler) {\n        FragmentActivity activity = getActivity();\n        if (activity == null) {\n            throw new RuntimeException(\"Fragment(\" + getClass().getSimpleName() + \") not attached to Activity.\");\n        }\n        ensureFragmentEffectRegistry();\n        return mFragmentEffectRegistry.register(lifecycleOwner, effectHandler);\n    }\n\n    public <T extends Effect> void notifyEffect(T effect) {\n        FragmentActivity activity = getActivity();\n        if (activity == null) {\n            QMUILog.d(TAG, \"Fragment(\" + getClass().getSimpleName() + \") not attached to Activity.\");\n            return;\n        }\n        ensureFragmentEffectRegistry();\n        mFragmentEffectRegistry.notifyEffect(effect);\n    }\n\n    private void ensureFragmentEffectRegistry() {\n        if (mFragmentEffectRegistry == null) {\n            QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n            ViewModelStoreOwner viewModelStoreOwner = provider != null ? provider.getContainerViewModelStoreOwner() : requireActivity();\n            mFragmentEffectRegistry = new ViewModelProvider(viewModelStoreOwner).get(QMUIFragmentEffectRegistry.class);\n        }\n    }\n\n    @Nullable\n    protected QMUIFragmentContainerProvider findFragmentContainerProvider(boolean includeSelf) {\n        Fragment current = includeSelf ? this : getParentFragment();\n        while (current != null) {\n            if (current instanceof QMUIFragmentContainerProvider) {\n                return (QMUIFragmentContainerProvider) current;\n            } else {\n                current = current.getParentFragment();\n            }\n        }\n        Activity activity = getActivity();\n        if (activity instanceof QMUIFragmentContainerProvider) {\n            return (QMUIFragmentContainerProvider) activity;\n        }\n        return null;\n    }\n\n    public int startFragmentAndDestroyCurrent(QMUIFragment fragment) {\n        return startFragmentAndDestroyCurrent(fragment, true);\n    }\n\n\n    /**\n     * start a new fragment and then destroy current fragment.\n     * assume there is a fragment stack(A->B->C), and you use this method to start a new\n     * fragment D and destroy fragment C. Now you are in fragment D, if you want call\n     * {@link #popBackStack()} to back to B, what the animation should be? Sometimes we hope run\n     * animation generated by transition B->C, but sometimes we hope run animation generated by\n     * transition C->D. this why second parameter exists.\n     *\n     * @param fragment                      new fragment to start\n     * @param useNewTransitionConfigWhenPop if true, use animation generated by transition C->D,\n     *                                      else, use animation generated by transition B->C\n     */\n    public int startFragmentAndDestroyCurrent(QMUIFragment fragment,\n                                                 boolean useNewTransitionConfigWhenPop) {\n        if (!checkStateLoss(\"startFragmentAndDestroyCurrent\")) {\n            return -1;\n        }\n\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(true);\n        if (provider == null) {\n            if (QMUIConfig.DEBUG) {\n                throw new RuntimeException(\"Can not find the fragment container provider.\");\n            } else {\n                Log.d(TAG, \"Can not find the fragment container provider.\");\n                return -1;\n            }\n        }\n\n        if(provider.getContainerFragmentManager().isDestroyed()){\n            return -1;\n        }\n\n        QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n        String tagName = fragment.getClass().getSimpleName();\n        FragmentManager fragmentManager = provider.getContainerFragmentManager();\n        FragmentTransaction transaction = fragmentManager.beginTransaction()\n                .setCustomAnimations(\n                        transitionConfig.enter, transitionConfig.exit,\n                        transitionConfig.popenter, transitionConfig.popout)\n                .setPrimaryNavigationFragment(null)\n                .replace(provider.getContextViewId(), fragment, tagName);\n        int index = transaction.commit();\n        Utils.modifyOpForStartFragmentAndDestroyCurrent(fragmentManager, fragment, useNewTransitionConfigWhenPop, transitionConfig);\n        return index;\n    }\n\n    /**\n     * start a new fragment and add to BackStack\n     * @param fragment the fragment to start\n     * @return Returns the identifier of this transaction's back stack entry,\n     * if {@link FragmentTransaction#addToBackStack(String)} had been called.  Otherwise, returns\n     * a negative number.\n     */\n    public int startFragment(QMUIFragment fragment) {\n        if (!checkStateLoss(\"startFragment\")) {\n            return -1;\n        }\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(true);\n        if (provider == null) {\n            if (QMUIConfig.DEBUG) {\n                throw new RuntimeException(\"Can not find the fragment container provider.\");\n            } else {\n                Log.d(TAG, \"Can not find the fragment container provider.\");\n                return -1;\n            }\n        }\n        return startFragment(fragment, provider);\n    }\n\n\n    public int startFragment(QMUIFragment... fragments){\n        if (!checkStateLoss(\"startFragment\")) {\n            return -1;\n        }\n        if(fragments.length == 0){\n            return -1;\n        }\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(true);\n        if (provider == null) {\n            if (QMUIConfig.DEBUG) {\n                throw new RuntimeException(\"Can not find the fragment container provider.\");\n            } else {\n                Log.d(TAG, \"Can not find the fragment container provider.\");\n                return -1;\n            }\n        }\n        if(provider.getContainerFragmentManager().isDestroyed()){\n            return -1;\n        }\n        if(fragments.length == 1){\n            return startFragment(fragments[0], provider);\n        }\n        ArrayList<FragmentTransaction> transactions = new ArrayList<>();\n        TransitionConfig lastTransitionConfig = fragments[fragments.length - 1].onFetchTransitionConfig();\n        boolean disableSwipeBack = false;\n        for (QMUIFragment fragment : fragments) {\n            FragmentTransaction transaction = provider.getContainerFragmentManager()\n                    .beginTransaction()\n                    .setPrimaryNavigationFragment(null);\n            TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n            if(disableSwipeBack){\n                fragment.mDisableSwipeBackByMutiStarted = true;\n            }\n            disableSwipeBack = true;\n            String tagName = fragment.getClass().getSimpleName();\n            transaction.setCustomAnimations(transitionConfig.enter, lastTransitionConfig.exit, transitionConfig.popenter, transitionConfig.popout);\n            transaction.replace(provider.getContextViewId(), fragment, tagName);\n            transaction.addToBackStack(tagName);\n            transactions.add(transaction);\n            transaction.setReorderingAllowed(true);\n        }\n        for(FragmentTransaction transaction: transactions){\n            transaction.commit();\n        }\n        return 0;\n    }\n\n    /**\n     * simulate the behavior of startActivityForResult/onActivityResult:\n     * 1. Jump fragment1 to fragment2 via startActivityForResult(fragment2, requestCode)\n     * 2. Pass data from fragment2 to fragment1 via setFragmentResult(RESULT_OK, data)\n     * 3. Get data in fragment1 through onFragmentResult(requestCode, resultCode, data)\n     *\n     * @deprecated use {@link #registerEffect} for a replacement\n     *\n     * @param fragment    target fragment\n     * @param requestCode request code\n     */\n    @Deprecated\n    public int startFragmentForResult(QMUIFragment fragment, int requestCode) {\n        if (!checkStateLoss(\"startFragmentForResult\")) {\n            return -1;\n        }\n        if (requestCode == NO_REQUEST_CODE) {\n            throw new RuntimeException(\"requestCode can not be \" + NO_REQUEST_CODE);\n        }\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(true);\n        if (provider == null) {\n            if (QMUIConfig.DEBUG) {\n                throw new RuntimeException(\"Can not find the fragment container provider.\");\n            } else {\n                Log.d(TAG, \"Can not find the fragment container provider.\");\n                return -1;\n            }\n        }\n\n        mSourceRequestCode = requestCode;\n        fragment.mTargetFragmentUUid = mUUid;\n        fragment.mTargetRequestCode = requestCode;\n        return startFragment(fragment, provider);\n    }\n\n    private int startFragment(QMUIFragment fragment, QMUIFragmentContainerProvider provider) {\n        if(provider.getContainerFragmentManager().isDestroyed()){\n            return -1;\n        }\n        QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n        String tagName = fragment.getClass().getSimpleName();\n        return provider.getContainerFragmentManager()\n                .beginTransaction()\n                .setPrimaryNavigationFragment(null)\n                .setCustomAnimations(transitionConfig.enter, transitionConfig.exit, transitionConfig.popenter, transitionConfig.popout)\n                .replace(provider.getContextViewId(), fragment, tagName)\n                .addToBackStack(tagName)\n                .commit();\n    }\n\n    /**\n     *\n     * @param resultCode\n     * @param data\n     *\n     * @deprecated use {@link #notifyEffect} for a replacement\n     */\n    @Deprecated\n    public void setFragmentResult(int resultCode, Intent data) {\n        if (mTargetRequestCode == NO_REQUEST_CODE) {\n            return;\n        }\n        notifyEffect(new FragmentResultEffect(mTargetFragmentUUid, resultCode, mTargetRequestCode, data));\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if(savedInstanceState != null){\n            mDisableSwipeBackByMutiStarted = savedInstanceState.getBoolean(QMUI_DISABLE_SWIPE_BACK_KEY, false);\n        }\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        if (mBaseView.getTag(R.id.qmui_arch_reused_layout) == null) {\n            onViewCreated(mBaseView);\n            mBaseView.setTag(R.id.qmui_arch_reused_layout, true);\n        }\n    }\n\n    private SwipeBackLayout newSwipeBackLayout() {\n        if(mCacheSwipeBackView != null && getParentFragment() != null){\n            if (mCacheSwipeBackView.getParent() != null) {\n                ((ViewGroup) mCacheSwipeBackView.getParent()).removeView(mCacheSwipeBackView);\n            }\n            if(mCacheSwipeBackView.getParent() == null){\n                initSwipeBackLayout(mCacheSwipeBackView);\n                return mCacheSwipeBackView;\n            }\n        }\n        View rootView = mCacheRootView;\n        if (rootView == null) {\n            rootView = onCreateView();\n            mCacheRootView = rootView;\n        } else {\n            if (rootView.getParent() != null) {\n                ((ViewGroup) rootView.getParent()).removeView(rootView);\n            }\n        }\n        SwipeBackLayout swipeBackLayout = SwipeBackLayout.wrap(rootView,\n                dragViewMoveAction(),\n                new SwipeBackLayout.Callback() {\n                    @Override\n                    public int getDragDirection(SwipeBackLayout swipeBackLayout, SwipeBackLayout.ViewMoveAction viewMoveAction, float downX, float downY, float dx, float dy, float touchSlop) {\n\n                        mCalled = false;\n                        if(mDisableSwipeBackByMutiStarted){\n                            return DRAG_DIRECTION_NONE;\n                        }\n                        boolean canHandle = canHandleSwipeBack();\n                        if (canHandle && !mCalled) {\n                            throw new RuntimeException(getClass().getSimpleName() + \" did not call through to super.shouldPreventSwipeBack()\");\n                        }\n\n                        if(!canHandle){\n                            return DRAG_DIRECTION_NONE;\n                        }\n                        return QMUIFragment.this.getDragDirection(\n                                swipeBackLayout, viewMoveAction, downX, downY, dx, dy, touchSlop);\n                    }\n\n                    @Override\n                    public void reportFrequentlyRequestLayout(int count, long duration) {\n                        QMUIFragment.this.reportFrequentlyRequestLayout(count, duration);\n                    }\n                });\n        initSwipeBackLayout(swipeBackLayout);\n        if(getParentFragment() != null){\n            mCacheSwipeBackView = swipeBackLayout;\n        }\n        return swipeBackLayout;\n    }\n\n    private void initSwipeBackLayout(SwipeBackLayout swipeBackLayout){\n        if(mListenerRemover != null){\n            mListenerRemover.remove();\n        }\n        mListenerRemover = swipeBackLayout.addSwipeListener(mSwipeListener);\n        swipeBackLayout.setOnInsetsHandler(new SwipeBackLayout.OnInsetsHandler() {\n            @Override\n            public int getInsetsType() {\n                return getRootViewInsetsType();\n            }\n        });\n        if (isCreateForSwipeBack) {\n            swipeBackLayout.setTag(R.id.fragment_container_view_tag, this);\n        }\n    }\n\n    private SwipeBackLayout.SwipeListener mSwipeListener = new SwipeBackLayout.SwipeListener() {\n\n        private QMUIFragment mModifiedFragment = null;\n\n        @Override\n        public void onScrollStateChange(int state, float scrollPercent) {\n            Log.i(TAG, \"SwipeListener:onScrollStateChange: state = \" + state + \" ;scrollPercent = \" + scrollPercent);\n            QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n            if (provider == null || provider.getFragmentContainerView() == null) {\n                return;\n            }\n            FragmentContainerView container = provider.getFragmentContainerView();\n            mIsInSwipeBack = state != SwipeBackLayout.STATE_IDLE;\n            if (state == SwipeBackLayout.STATE_IDLE) {\n                if (mSwipeBackgroundView != null) {\n                    if (scrollPercent <= 0.0F) {\n                        mSwipeBackgroundView.unBind();\n                        mSwipeBackgroundView = null;\n                    } else if (scrollPercent >= 1.0F) {\n                        // unbind mSwipeBackgroundView util onDestroy\n                        Activity activity = getActivity();\n                        if (activity != null) {\n                            sPopBackWhenSwipeFinished = true;\n                            // must call before popBackStack. mSwipeBackgroundView maybe released in popBackStack\n                            int exitAnim = mSwipeBackgroundView.hasChildWindow() ?\n                                    R.anim.swipe_back_exit_still : R.anim.swipe_back_exit;\n                            popBackStack();\n                            activity.overridePendingTransition(R.anim.swipe_back_enter, exitAnim);\n                            sPopBackWhenSwipeFinished = false;\n                        }\n                    }\n                    return;\n                }\n                if (scrollPercent <= 0.0F) {\n                    handleSwipeBackCancelOrFinished(container);\n                } else if (scrollPercent >= 1.0F) {\n                    handleSwipeBackCancelOrFinished(container);\n                    FragmentManager fragmentManager = provider.getContainerFragmentManager();\n                    Utils.findAndModifyOpInBackStackRecord(fragmentManager, -1, new Utils.OpHandler() {\n                        @Override\n                        public boolean handle(Object op) {\n                            Field cmdField = Utils.getOpCmdField(op);\n                            if (cmdField == null) {\n                                return false;\n                            }\n                            try {\n                                cmdField.setAccessible(true);\n                                int cmd = (int) cmdField.get(op);\n                                if (cmd == 1) {\n                                    Field popEnterAnimField = Utils.getOpPopExitAnimField(op);\n                                    if (popEnterAnimField != null) {\n                                        popEnterAnimField.setAccessible(true);\n                                        popEnterAnimField.set(op, 0);\n                                    }\n                                } else if (cmd == 3) {\n                                    Field popExitAnimField = Utils.getOpPopEnterAnimField(op);\n                                    if (popExitAnimField != null) {\n                                        popExitAnimField.setAccessible(true);\n                                        popExitAnimField.set(op, 0);\n                                    }\n                                }\n                            } catch (IllegalAccessException e) {\n                                e.printStackTrace();\n                            }\n                            return false;\n                        }\n\n                        @Override\n                        public boolean needReNameTag() {\n                            return false;\n                        }\n\n                        @Override\n                        public String newTagName() {\n                            return null;\n                        }\n                    });\n                    sPopBackWhenSwipeFinished = true;\n                    popBackStack();\n                    sPopBackWhenSwipeFinished = false;\n                }\n            }\n        }\n\n        @Override\n        public void onScroll(int dragDirection, int moveEdge, float scrollPercent) {\n            scrollPercent = Math.max(0f, Math.min(1f, scrollPercent));\n            QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n            if (provider == null || provider.getFragmentContainerView() == null) {\n                return;\n            }\n            FragmentContainerView container = provider.getFragmentContainerView();\n            int targetOffset = (int) (Math.abs(\n                    backViewInitOffset(container.getContext(), dragDirection, moveEdge)) * (1 - scrollPercent));\n            int childCount = container.getChildCount();\n            for (int i = childCount - 1; i >= 0; i--) {\n                View view = container.getChildAt(i);\n                Object tag = view.getTag(R.id.qmui_arch_swipe_layout_in_back);\n                if (SWIPE_BACK_VIEW.equals(tag)) {\n                    SwipeBackLayout.translateInSwipeBack(view, moveEdge, targetOffset);\n                }\n            }\n            if (mSwipeBackgroundView != null) {\n                SwipeBackLayout.translateInSwipeBack(mSwipeBackgroundView, moveEdge, targetOffset);\n            }\n        }\n\n        @SuppressLint(\"PrivateApi\")\n        @Override\n        public void onSwipeBackBegin(final int dragDirection, final int moveEdge) {\n            Log.i(TAG, \"SwipeListener:onSwipeBackBegin: moveEdge = \" + moveEdge);\n            QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n            if (provider == null || provider.getFragmentContainerView() == null) {\n                return;\n            }\n            final FragmentContainerView container = provider.getFragmentContainerView();\n\n            QMUIKeyboardHelper.hideKeyboard(mBaseView);\n            onDragStart();\n            FragmentManager fragmentManager = provider.getContainerFragmentManager();\n            int backStackCount = fragmentManager.getBackStackEntryCount();\n            if (backStackCount > 1 && !mFinishActivityIfOnBackPressed) {\n                Utils.findAndModifyOpInBackStackRecord(fragmentManager, -1, new Utils.OpHandler() {\n                    @Override\n                    public boolean handle(Object op) {\n                        Field cmdField = Utils.getOpCmdField(op);\n                        if (cmdField == null) {\n                            return false;\n                        }\n                        try {\n                            cmdField.setAccessible(true);\n                            int cmd = (int) cmdField.get(op);\n                            if (cmd == 3) {\n                                Field fragmentField = Utils.getOpFragmentField(op);\n                                if (fragmentField != null) {\n                                    fragmentField.setAccessible(true);\n                                    Object fragmentObject = fragmentField.get(op);\n                                    if (fragmentObject instanceof QMUIFragment) {\n                                        mModifiedFragment = (QMUIFragment) fragmentObject;\n                                        mModifiedFragment.isCreateForSwipeBack = true;\n                                        View baseView = mModifiedFragment.onCreateView(LayoutInflater.from(getContext()), container, null);\n                                        mModifiedFragment.isCreateForSwipeBack = false;\n                                        if (baseView != null) {\n                                            addViewInSwipeBack(container, baseView, 0);\n                                            handleChildFragmentListWhenSwipeBackStart(mModifiedFragment, baseView);\n                                            SwipeBackLayout.translateInSwipeBack(baseView, moveEdge,\n                                                    Math.abs(backViewInitOffset(baseView.getContext(), dragDirection, moveEdge)));\n                                        }\n                                    }\n                                }\n                            }\n                        } catch (IllegalAccessException e) {\n                            e.printStackTrace();\n                        }\n                        return false;\n                    }\n\n                    @Override\n                    public boolean needReNameTag() {\n                        return false;\n                    }\n\n                    @Override\n                    public String newTagName() {\n                        return null;\n                    }\n                });\n            } else if (getParentFragment() == null) {\n                Activity currentActivity = getActivity();\n                if (currentActivity != null) {\n                    ViewGroup decorView = (ViewGroup) currentActivity.getWindow().getDecorView();\n                    Activity prevActivity = QMUISwipeBackActivityManager.getInstance()\n                            .getPenultimateActivity(currentActivity);\n                    if(prevActivity == null){\n                        return;\n                    }\n                    if (decorView.getChildAt(0) instanceof SwipeBackgroundView) {\n                        mSwipeBackgroundView = (SwipeBackgroundView) decorView.getChildAt(0);\n                    } else {\n                        mSwipeBackgroundView = new SwipeBackgroundView(getContext(), forceDisableHardwareAcceleratedForSwipeBackground());\n                        decorView.addView(mSwipeBackgroundView, 0, new FrameLayout.LayoutParams(\n                                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n                    }\n                    mSwipeBackgroundView.bind(prevActivity, currentActivity, restoreSubWindowWhenDragBack());\n                    SwipeBackLayout.translateInSwipeBack(mSwipeBackgroundView, moveEdge,\n                            Math.abs(backViewInitOffset(decorView.getContext(), dragDirection, moveEdge)));\n                }\n            }\n        }\n\n        @Override\n        public void onScrollOverThreshold() {\n            Log.i(TAG, \"SwipeListener:onEdgeTouch:onScrollOverThreshold\");\n        }\n\n        private void addViewInSwipeBack(ViewGroup parent, View child) {\n            addViewInSwipeBack(parent, child, -1);\n        }\n\n        private void addViewInSwipeBack(ViewGroup parent, View child, int index) {\n            if (parent != null && child != null) {\n                child.setTag(R.id.qmui_arch_swipe_layout_in_back, SWIPE_BACK_VIEW);\n                parent.addView(child, index);\n            }\n        }\n\n        private void removeViewInSwipeBack(ViewGroup parent, Function<View, Void> onRemove) {\n            if (parent != null) {\n                int childCount = parent.getChildCount();\n                for (int i = childCount - 1; i >= 0; i--) {\n                    View view = parent.getChildAt(i);\n                    Object tag = view.getTag(R.id.qmui_arch_swipe_layout_in_back);\n                    if (SWIPE_BACK_VIEW.equals(tag)) {\n                        if (onRemove != null) {\n                            onRemove.apply(view);\n                        }\n                        view.setTranslationY(0);\n                        view.setTranslationX(0);\n                        parent.removeView(view);\n                    }\n                }\n            }\n        }\n\n\n        private void handleChildFragmentListWhenSwipeBackStart(Fragment parentFragment, View baseView) throws IllegalAccessException {\n            // handle issue #235\n            if (baseView instanceof ViewGroup) {\n                ViewGroup childMainContainer = (ViewGroup) baseView;\n                FragmentManager childFragmentManager = parentFragment.getChildFragmentManager();\n                List<Fragment> childFragmentList = childFragmentManager.getFragments();\n                int childContainerId = 0;\n                ViewGroup childContainer = null;\n                for (Fragment fragment : childFragmentList) {\n                    if (fragment instanceof QMUIFragment) {\n                        QMUIFragment qmuiFragment = (QMUIFragment) fragment;\n                        Field containerIdField = null;\n                        try {\n                            containerIdField = Fragment.class.getDeclaredField(\"mContainerId\");\n                        } catch (NoSuchFieldException e) {\n                            continue;\n                        }\n                        containerIdField.setAccessible(true);\n                        int containerId = containerIdField.getInt(qmuiFragment);\n                        if (containerId != 0) {\n                            if (childContainerId != containerId) {\n                                childContainerId = containerId;\n                                childContainer = childMainContainer.findViewById(containerId);\n                            }\n                            if (childContainer != null) {\n                                qmuiFragment.isCreateForSwipeBack = true;\n                                View childView = fragment.onCreateView(\n                                        LayoutInflater.from(childContainer.getContext()), childContainer, null);\n                                qmuiFragment.isCreateForSwipeBack = false;\n                                addViewInSwipeBack(childContainer, childView);\n                                handleChildFragmentListWhenSwipeBackStart(fragment, childView);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n\n        private void handleSwipeBackCancelOrFinished(ViewGroup container) {\n            removeViewInSwipeBack(container, new Function<View, Void>() {\n                @Override\n                public Void apply(View input) {\n                    if (mModifiedFragment == null) {\n                        return null;\n                    }\n                    if (input instanceof ViewGroup) {\n                        ViewGroup childMainContainer = (ViewGroup) input;\n                        FragmentManager childFragmentManager = mModifiedFragment.getChildFragmentManager();\n                        List<Fragment> childFragmentList = childFragmentManager.getFragments();\n                        int childContainerId = 0;\n                        try {\n                            for (Fragment fragment : childFragmentList) {\n                                if (fragment instanceof QMUIFragment) {\n                                    QMUIFragment qmuiFragment = (QMUIFragment) fragment;\n                                    Field containerIdField = Fragment.class.getDeclaredField(\"mContainerId\");\n                                    containerIdField.setAccessible(true);\n                                    int containerId = containerIdField.getInt(qmuiFragment);\n                                    if (containerId != 0 && childContainerId != containerId) {\n                                        childContainerId = containerId;\n                                        ViewGroup childContainer = childMainContainer.findViewById(containerId);\n                                        removeViewInSwipeBack(childContainer, null);\n                                    }\n                                }\n                            }\n                        } catch (IllegalAccessException e) {\n                            e.printStackTrace();\n                        } catch (NoSuchFieldException e) {\n                            e.printStackTrace();\n                        }\n\n                    }\n                    return null;\n                }\n            });\n            mModifiedFragment = null;\n        }\n    };\n\n    public boolean isInSwipeBack() {\n        return mIsInSwipeBack;\n    }\n\n    protected boolean forceDisableHardwareAcceleratedForSwipeBackground(){\n        return false;\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        SwipeBackLayout swipeBackLayout = newSwipeBackLayout();\n        if (!isCreateForSwipeBack) {\n            mBaseView = swipeBackLayout.getContentView();\n            swipeBackLayout.setTag(R.id.qmui_arch_swipe_layout_in_back, null);\n        }\n\n        swipeBackLayout.setFitsSystemWindows(false);\n        return swipeBackLayout;\n    }\n\n    private void bubbleBackPressedEvent() {\n        // disable this and go with FragmentManager's backPressesCallback\n        // because it will call execPendingActions before popBackStackImmediate\n        mOnBackPressedCallback.setEnabled(false);\n        mOnBackPressedDispatcher.onBackPressed();\n        mOnBackPressedCallback.setEnabled(true);\n    }\n\n    protected final void onNormalBackPressed() {\n        runSideEffectOnNormalBackPressed();\n        if (getParentFragment() != null) {\n            bubbleBackPressedEvent();\n            return;\n        }\n\n        FragmentActivity activity = requireActivity();\n        if (activity instanceof QMUIFragmentContainerProvider) {\n            QMUIFragmentContainerProvider provider = (QMUIFragmentContainerProvider) activity;\n            if ((provider.getContainerFragmentManager().getBackStackEntryCount() > 1 && !mFinishActivityIfOnBackPressed) || provider.getContainerFragmentManager().getPrimaryNavigationFragment() == this) {\n                bubbleBackPressedEvent();\n            } else {\n                QMUIFragment.TransitionConfig transitionConfig = onFetchTransitionConfig();\n                if (needInterceptLastFragmentFinish()) {\n                    if(!sPopBackWhenSwipeFinished){\n                        activity.finishAfterTransition();\n                    }else{\n                        activity.finish();\n                    }\n                    activity.overridePendingTransition(transitionConfig.popenterAnimation, transitionConfig.popoutAnimation);\n                    return;\n                }\n                Object toExec = onLastFragmentFinish();\n                if (toExec != null) {\n                    if (toExec instanceof QMUIFragment) {\n                        QMUIFragment fragment = (QMUIFragment) toExec;\n                        startFragmentAndDestroyCurrent(fragment, false);\n                    } else if (toExec instanceof Intent) {\n                        Intent intent = (Intent) toExec;\n                        startActivity(intent);\n                        activity.overridePendingTransition(transitionConfig.popenterAnimation, transitionConfig.popoutAnimation);\n                        activity.finish();\n                    } else {\n                        onHandleSpecLastFragmentFinish(activity, transitionConfig, toExec);\n                    }\n                } else {\n                    if(!sPopBackWhenSwipeFinished){\n                        activity.finishAfterTransition();\n                    }else{\n                        activity.finish();\n                    }\n                    activity.overridePendingTransition(transitionConfig.popenterAnimation, transitionConfig.popoutAnimation);\n                }\n            }\n        } else {\n            bubbleBackPressedEvent();\n        }\n    }\n\n    protected void runSideEffectOnNormalBackPressed() {\n\n    }\n\n    protected void onBackPressed() {\n        onNormalBackPressed();\n    }\n\n    protected void reportFrequentlyRequestLayout(int count, long duration){\n        QMUILog.w(TAG, \"requestLayout is too frequent(requestLayout \" + count + \"times within \" + duration + \"ms\");\n    }\n\n    protected void onHandleSpecLastFragmentFinish(FragmentActivity fragmentActivity,\n                                                  QMUIFragment.TransitionConfig transitionConfig,\n                                                  Object toExec) {\n        fragmentActivity.finish();\n        fragmentActivity.overridePendingTransition(transitionConfig.popenterAnimation, transitionConfig.popoutAnimation);\n    }\n\n    /**\n     * pop back\n     */\n    protected void popBackStack() {\n        if (mOnBackPressedDispatcher != null) {\n            mOnBackPressedDispatcher.onBackPressed();\n        }\n    }\n\n    /**\n     * pop back to a clazz type fragment\n     * <p>\n     * Assuming there is a back stack: Home -> List -> Detail. Perform popBackStack(Home.class),\n     * Home is the current fragment\n     * <p>\n     * if the clazz type fragment doest not exist in back stack, this method is Equivalent\n     * to popBackStack()\n     *\n     * @param cls the type of target fragment\n     */\n    protected void popBackStack(Class<? extends QMUIFragment> cls) {\n        if (checkPopBack()) {\n            getParentFragmentManager().popBackStack(cls.getSimpleName(), 0);\n        }\n    }\n\n    /**\n     * pop back to a non-class type Fragment\n     *\n     * @param cls the target fragment class type\n     */\n    protected void popBackStackInclusive(Class<? extends QMUIFragment> cls) {\n        if (checkPopBack()) {\n            getParentFragmentManager().popBackStack(cls.getSimpleName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);\n        }\n    }\n\n    private boolean checkPopBack() {\n        if (!isResumed() || mEnterAnimationStatus != ANIMATION_ENTER_STATUS_END) {\n            return false;\n        }\n        return checkStateLoss(\"popBackStack\");\n    }\n\n    protected void popBackStackAfterResume() {\n        if (isResumed() && mEnterAnimationStatus == ANIMATION_ENTER_STATUS_END) {\n            popBackStack();\n        } else {\n            runAfterAnimation(new Runnable() {\n                @Override\n                public void run() {\n                    if (isResumed()) {\n                        popBackStack();\n                    } else {\n                        runAfterResumed(new Runnable() {\n                            @Override\n                            public void run() {\n                                popBackStack();\n                            }\n                        });\n                    }\n                }\n            }, true);\n        }\n    }\n\n    private boolean checkStateLoss(String logName) {\n        if (!isAdded()) {\n            return false;\n        }\n        FragmentManager fragmentManager = getParentFragmentManager();\n        if (fragmentManager.isStateSaved()) {\n            QMUILog.d(TAG, logName + \" can not be invoked after onSaveInstanceState\");\n            return false;\n        }\n        return true;\n    }\n\n    @Nullable\n    @Override\n    public final Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {\n        return null;\n    }\n\n    @Nullable\n    @Override\n    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {\n        if(enter && nextAnim != 0){\n            Animator animator = AnimatorInflater.loadAnimator(getContext(), nextAnim);\n            animator.addListener(new AnimatorListenerAdapter() {\n\n                @Override\n                public void onAnimationStart(Animator animation) {\n                    checkAndCallOnEnterAnimationStart(animation);\n                }\n\n                @Override\n                public void onAnimationEnd(Animator animation) {\n                    checkAndCallOnEnterAnimationEnd(animation);\n                }\n            });\n            return animator;\n        }\n        return super.onCreateAnimator(transit, enter, nextAnim);\n    }\n\n    private void checkAndCallOnEnterAnimationStart(@Nullable Animator animation) {\n        mCalled = false;\n        onEnterAnimationStart(animation);\n        if (!mCalled) {\n            throw new RuntimeException(getClass().getSimpleName() + \" did not call through to super.onEnterAnimationStart(Animation)\");\n        }\n    }\n\n    private void checkAndCallOnEnterAnimationEnd(@Nullable Animator animation) {\n        mCalled = false;\n        onEnterAnimationEnd(animation);\n        if (!mCalled) {\n            throw new RuntimeException(getClass().getSimpleName() + \" did not call through to super.onEnterAnimationEnd(Animation)\");\n        }\n    }\n\n    /**\n     * onCreateView\n     */\n    protected abstract View onCreateView();\n\n\n    /**\n     * Corresponding to {@link #onCreateView()}, it called only when new UI (not cached UI)\n     * is created by {@link #onCreateView()}.\n     * It may be used to bind views to fragment and dynamically create child views such as\n     * {@link QMUITopBar#addLeftBackImageButton()}\n     *\n     * @param rootView the view created by {@link #onCreateView()}\n     */\n    protected void onViewCreated(@NonNull View rootView) {\n\n    }\n\n    /**\n     * Will be performed in onStart\n     *\n     * @param requestCode request code\n     * @param resultCode  result code\n     * @param data        extra data\n     *\n     * @deprecated use {@link #registerEffect} for a replacement\n     */\n    @Deprecated\n    protected void onFragmentResult(int requestCode, int resultCode, Intent data) {\n\n    }\n\n    /**\n     * disable or enable drag back\n     *\n     * @return if true open dragBack, otherwise close dragBack\n     * @deprecated Use {@link #getDragDirection(SwipeBackLayout, SwipeBackLayout.ViewMoveAction, float, float, float, float, float)}\n     */\n    @Deprecated\n    protected boolean canDragBack() {\n        return true;\n    }\n\n\n    /**\n     * disable or enable drag back\n     * @param context context\n     * @param dragDirection gesture direction\n     * @param moveEdge view move edge\n     * @return if true open dragBack, otherwise close dragBack\n     *\n     * @deprecated Use {@link #getDragDirection(SwipeBackLayout, SwipeBackLayout.ViewMoveAction, float, float, float, float, float)}\n     */\n    @Deprecated\n    protected boolean canDragBack(Context context, int dragDirection, int moveEdge) {\n        return canDragBack();\n    }\n\n    /**\n     * @return the init offset for backView for Parallax scrolling\n     * @deprecated Use {@link #backViewInitOffset(Context, int, int)}\n     */\n    @Deprecated\n    protected int backViewInitOffset() {\n        return 0;\n    }\n\n    protected int backViewInitOffset(Context context, int dragDirection, int moveEdge) {\n        return backViewInitOffset();\n    }\n\n    /**\n     * called when drag back started.\n     */\n    protected void onDragStart() {\n\n    }\n\n    /**\n     * @return\n     * @deprecated Use {@link #dragBackDirection()}\n     */\n    @Deprecated\n    protected int dragBackEdge() {\n        return EDGE_LEFT;\n    }\n\n    /**\n     *\n     * @return\n     * @deprecated Use {@link #getDragDirection(SwipeBackLayout, SwipeBackLayout.ViewMoveAction, float, float, float, float, float)}\n     */\n    @Deprecated\n    protected int dragBackDirection() {\n        int oldEdge = dragBackEdge();\n        if (oldEdge == EDGE_RIGHT) {\n            return SwipeBackLayout.DRAG_DIRECTION_RIGHT_TO_LEFT;\n        } else if (oldEdge == EDGE_TOP) {\n            return SwipeBackLayout.DRAG_DIRECTION_TOP_TO_BOTTOM;\n        } else if (oldEdge == EDGE_BOTTOM) {\n            return SwipeBackLayout.DRAG_DIRECTION_BOTTOM_TO_TOP;\n        }\n        return SwipeBackLayout.DRAG_DIRECTION_LEFT_TO_RIGHT;\n    }\n\n    protected SwipeBackLayout.ViewMoveAction dragViewMoveAction() {\n        return SwipeBackLayout.MOVE_VIEW_AUTO;\n    }\n\n    protected boolean canHandleSwipeBack(){\n        mCalled = true;\n        // 1. can not swipe back if enter animation is not finished\n        if (mEnterAnimationStatus != ANIMATION_ENTER_STATUS_END) {\n            return false;\n        }\n\n        Activity activity = getActivity();\n        if(activity == null || activity.isFinishing()){\n            return false;\n        }\n\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n        if (provider == null) {\n            return false;\n        }\n        FragmentManager fragmentManager = provider.getContainerFragmentManager();\n\n        // 3. is not managed by QMUIFragmentContainerProvider\n        if (fragmentManager == null || fragmentManager != getParentFragmentManager()) {\n            return false;\n        }\n\n        // 4. should handle by child\n        if(provider.isChildHandlePopBackRequested()){\n            return false;\n        }\n\n        // 5. can not swipe back if the view is null\n        View view = getView();\n        if (view == null) {\n            return false;\n        }\n\n        // 6. can not swipe back if the backStack entry count is less than 2\n        if ((fragmentManager.getBackStackEntryCount() <= 1 || mFinishActivityIfOnBackPressed) &&\n                !QMUISwipeBackActivityManager.getInstance().canSwipeBack(activity)) {\n            return false;\n        }\n\n        return true;\n    }\n\n    public void setFinishActivityIfOnBackPressed(boolean finishActivityIfOnBackPressed) {\n        mFinishActivityIfOnBackPressed = finishActivityIfOnBackPressed;\n    }\n\n    protected int getDragDirection(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull SwipeBackLayout.ViewMoveAction viewMoveAction,\n                                   float downX, float downY, float dx, float dy, float slopTouch) {\n        int targetDirection = dragBackDirection();\n        if (!canDragBack(swipeBackLayout.getContext(), targetDirection, viewMoveAction.getEdge(targetDirection))) {\n            return DRAG_DIRECTION_NONE;\n        }\n        int edgeSize = QMUIDisplayHelper.dp2px(swipeBackLayout.getContext(), 20);\n        if (targetDirection == DRAG_DIRECTION_LEFT_TO_RIGHT) {\n            if (downX < edgeSize && dx >= slopTouch) {\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_RIGHT_TO_LEFT) {\n            if (downX > swipeBackLayout.getWidth() - edgeSize && -dx >= slopTouch) {\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_TOP_TO_BOTTOM) {\n            if (downY < edgeSize && dy >= slopTouch) {\n                return targetDirection;\n            }\n        } else if (targetDirection == DRAG_DIRECTION_BOTTOM_TO_TOP) {\n            if (downY > swipeBackLayout.getHeight() - edgeSize && -dy >= slopTouch) {\n                return targetDirection;\n            }\n        }\n\n        return DRAG_DIRECTION_NONE;\n    }\n\n    /**\n     * the action will be performed before the start of the enter animation start or after the\n     * enter animation is finished\n     *\n     * @param runnable the action to perform\n     */\n    public void runAfterAnimation(Runnable runnable) {\n        runAfterAnimation(runnable, false);\n    }\n\n    /**\n     * When data is rendered duration the transition animation, it will cause a choppy. this method\n     * will promise the data is rendered before or after transition animation\n     *\n     * @param runnable the action to perform\n     * @param onlyEnd  if true, the action is only performed after the enter animation is finished,\n     *                 otherwise it can be performed before the start of the enter animation start\n     *                 or after the enter animation is finished.\n     */\n    public void runAfterAnimation(Runnable runnable, boolean onlyEnd) {\n        Utils.assertInMainThread();\n        boolean ok = onlyEnd ? mEnterAnimationStatus == ANIMATION_ENTER_STATUS_END :\n                mEnterAnimationStatus != ANIMATION_ENTER_STATUS_STARTED;\n        if (ok) {\n            runnable.run();\n        } else {\n            if (mDelayRenderRunnableList == null) {\n                mDelayRenderRunnableList = new ArrayList<>(4);\n            }\n            mDelayRenderRunnableList.add(runnable);\n        }\n    }\n\n    /**\n     * some action, such as {@link #popBackStack()}, can not't invoked duration fragment-lifecycle,\n     * then we can call this method to ensure these actions is invoked after resumed.\n     * one use case is to call {@link #popBackStackAfterResume()} in {@link #onFragmentResult(int, int, Intent)}\n     *\n     * @param runnable\n     */\n    public void runAfterResumed(Runnable runnable) {\n        Utils.assertInMainThread();\n        if (isResumed()) {\n            runnable.run();\n        } else {\n            if (mPostResumeRunnableList == null) {\n                mPostResumeRunnableList = new ArrayList<>(4);\n            }\n            mPostResumeRunnableList.add(runnable);\n        }\n    }\n\n    /**\n     * may not be call.\n     * @param animation\n     */\n    protected void onEnterAnimationStart(@Nullable Animator animation) {\n        if (mCalled) {\n            throw new IllegalAccessError(\"don't call #onEnterAnimationStart() directly\");\n        }\n        mCalled = true;\n        mEnterAnimationStatus = ANIMATION_ENTER_STATUS_STARTED;\n        isInEnterAnimationLiveData.setValue(true);\n    }\n\n    /**\n     * may not be call.\n     * @param animation\n     */\n    protected void onEnterAnimationEnd(@Nullable Animator animation) {\n        if (mCalled) {\n            throw new IllegalAccessError(\"don't call #onEnterAnimationEnd() directly\");\n        }\n        mCalled = true;\n        mEnterAnimationStatus = ANIMATION_ENTER_STATUS_END;\n        isInEnterAnimationLiveData.setValue(false);\n        notifyDelayRenderRunnableList();\n    }\n\n    private void notifyDelayRenderRunnableList(){\n        if (mDelayRenderRunnableList != null) {\n            ArrayList<Runnable> list = mDelayRenderRunnableList;\n            mDelayRenderRunnableList = null;\n            if (!list.isEmpty()) {\n                for (Runnable runnable : list) {\n                    runnable.run();\n                }\n            }\n        }\n    }\n\n    public LiveData<Boolean> getIsInEnterAnimationLiveData() {\n        return isInEnterAnimationLiveData;\n    }\n\n    protected <T> LiveData<T> enterAnimationAvoidTransform(final LiveData<T> origin){\n        return enterAnimationAvoidTransform(origin, isInEnterAnimationLiveData);\n    }\n\n    protected <T> LiveData<T> enterAnimationAvoidTransform(final LiveData<T> origin, LiveData<Boolean> enterAnimationLiveData){\n        final MediatorLiveData<T> result = new MediatorLiveData<T>();\n        result.addSource(enterAnimationLiveData, new Observer<Boolean>(){\n\n            boolean isAdded = false;\n            @Override\n            public void onChanged(Boolean isInEnterAnimation) {\n                if(isInEnterAnimation){\n                    isAdded = false;\n                    result.removeSource(origin);\n                }else {\n                    if(!isAdded){\n                        isAdded = true;\n                        result.addSource(origin, new Observer<T>() {\n                            @Override\n                            public void onChanged(T t) {\n                                result.setValue(t);\n                            }\n                        });\n                    }\n                }\n            }\n        });\n        return result;\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mSwipeBackgroundView != null) {\n            mSwipeBackgroundView.unBind();\n            mSwipeBackgroundView = null;\n        }\n\n        // help gc, sometimes user may hold fragment instance in somewhere,\n        // then these objects can not be released in time.\n        mCacheRootView = null;\n        mDelayRenderRunnableList = null;\n        mCheckPostResumeRunnable = null;\n    }\n\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        return false;\n    }\n\n    public boolean onKeyUp(int keyCode, KeyEvent event) {\n        return false;\n    }\n\n\n    @WindowInsetsCompat.Type.InsetsType\n    public int getRootViewInsetsType() {\n        return getParentFragment() == null ? WindowInsetsCompat.Type.ime() : 0;\n    }\n\n    @Override\n    public void refreshFromScheme(@Nullable Bundle bundle) {\n\n    }\n\n    /**\n     * When finishing to pop back last fragment, let activity have a chance to do something\n     * like start a new fragment\n     *\n     * @return QMUIFragment to start a new fragment or Intent to start a new Activity\n     */\n    @SuppressWarnings(\"SameReturnValue\")\n    public Object onLastFragmentFinish() {\n        return null;\n    }\n\n    /**\n     * if intercepted, onLastFragmentFinish will not be invoked.\n     * @return\n     */\n    protected boolean needInterceptLastFragmentFinish(){\n        Activity activity = getActivity();\n        return activity == null || !activity.isTaskRoot();\n    }\n\n    /**\n     * restore sub window(e.g dialog) when drag back to previous activity\n     *\n     * @return\n     */\n    protected boolean restoreSubWindowWhenDragBack() {\n        return true;\n    }\n\n\n    public final boolean isStartedByScheme() {\n        Bundle arguments = getArguments();\n        return arguments != null && arguments.getBoolean(QMUISchemeHandler.ARG_FROM_SCHEME, false);\n    }\n\n\n    /**\n     * Fragment Transition Controller\n     */\n    public TransitionConfig onFetchTransitionConfig() {\n        return SLIDE_TRANSITION_CONFIG;\n    }\n\n\n    public static final class TransitionConfig {\n        public final int enter;\n        public final int exit;\n        public final int popenter;\n        public final int popout;\n        public final int popenterAnimation;\n        public final int popoutAnimation;\n\n        public TransitionConfig(\n                int enter, int exit,\n                int popenter, int popout,\n                int popenterAnimation, int popoutAnimation\n        ) {\n            this.enter = enter;\n            this.exit = exit;\n            this.popenter = popenter;\n            this.popout = popout;\n\n            // only use for pop activity if only one fragment exist.\n            this.popenterAnimation = popenterAnimation;\n            this.popoutAnimation = popoutAnimation;\n        }\n    }\n}\n\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragmentActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentContainerView;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.lifecycle.ViewModelStoreOwner;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.arch.annotation.DefaultFirstFragment;\nimport com.qmuiteam.qmui.util.QMUIStatusBarHelper;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * the container activity for {@link QMUIFragment}.\n * Created by cgspine on 15/9/14.\n */\npublic abstract class QMUIFragmentActivity extends InnerBaseActivity implements QMUIFragmentContainerProvider {\n    public static final String QMUI_INTENT_DST_FRAGMENT_NAME = \"qmui_intent_dst_fragment_name\";\n    public static final String QMUI_INTENT_FRAGMENT_ARG = \"qmui_intent_fragment_arg\";\n    public static final String QMUI_INTENT_FRAGMENT_LIST_ARG = \"qmui_intent_fragment_list_arg\";\n    public static final String QMUI_MUTI_START_INDEX = \"qmui_muti_start_index\";\n    private static final String TAG = \"QMUIFragmentActivity\";\n    private RootView mRootView;\n    private FragmentAutoInitResult mFragmentAutoInitResult = FragmentAutoInitResult.unHandled;\n    private boolean isChildHandlePopBackRequested = false;\n\n    @Override\n    public int getContextViewId() {\n        return R.id.qmui_activity_fragment_container_id;\n    }\n\n    @Override\n    public FragmentManager getContainerFragmentManager() {\n        return getSupportFragmentManager();\n    }\n\n    public RootView getRootView() {\n        return mRootView;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        performTranslucent();\n        mRootView = onCreateRootView(getContextViewId());\n        setContentView(mRootView);\n        if (savedInstanceState == null) {\n            long start = System.currentTimeMillis();\n            Intent intent = getIntent();\n\n            // 1. handle muti fragments\n            mFragmentAutoInitResult = instantiationMutiFragment(intent);\n\n            if (mFragmentAutoInitResult == FragmentAutoInitResult.unHandled) {\n                try {\n                    Class<? extends QMUIFragment> firstFragmentClass = null;\n                    // 2. try get first fragment from fragment class name\n                    String fragmentClassName = intent.getStringExtra(QMUI_INTENT_DST_FRAGMENT_NAME);\n                    if (fragmentClassName != null && !fragmentClassName.isEmpty()) {\n                        firstFragmentClass = (Class<? extends QMUIFragment>) Class.forName(fragmentClassName);\n                    }\n\n                    // 3. try get fragment from annotation @DefaultFirstFragment\n                    if (firstFragmentClass == null) {\n                        firstFragmentClass = getDefaultFirstFragment();\n                    }\n\n                    if (firstFragmentClass != null) {\n                        QMUIFragment firstFragment = instantiationFragment(firstFragmentClass, intent.getBundleExtra(QMUI_INTENT_FRAGMENT_ARG));\n                        if (firstFragment != null) {\n                            getSupportFragmentManager()\n                                    .beginTransaction()\n                                    .add(getContextViewId(), firstFragment, firstFragment.getClass().getSimpleName())\n                                    .addToBackStack(firstFragment.getClass().getSimpleName())\n                                    .commit();\n                            mFragmentAutoInitResult = FragmentAutoInitResult.success;\n                        }\n                    }\n                } catch (Exception e) {\n                    QMUILog.d(TAG, \"fragment auto inited: \" + e.getMessage());\n                    mFragmentAutoInitResult = FragmentAutoInitResult.failed;\n                }\n\n            }\n            Log.i(TAG, \"the time it takes to inject first fragment from annotation is \" + (System.currentTimeMillis() - start));\n        }\n    }\n\n    protected FragmentAutoInitResult instantiationMutiFragment(Intent intent) {\n        List<Bundle> fragmentBundles = intent.getParcelableArrayListExtra(QMUI_INTENT_FRAGMENT_LIST_ARG);\n        if (fragmentBundles != null && fragmentBundles.size() > 0) {\n            List<QMUIFragment> fragments = new ArrayList<>(fragmentBundles.size());\n            for (Bundle bundle : fragmentBundles) {\n                String fragmentClassName = bundle.getString(QMUI_INTENT_DST_FRAGMENT_NAME);\n                try {\n                    Class<? extends QMUIFragment> cls = (Class<? extends QMUIFragment>) Class.forName(fragmentClassName);\n                    QMUIFragment fragment = instantiationFragment(cls, bundle.getBundle(QMUI_INTENT_FRAGMENT_ARG));\n                    if (fragment == null) {\n                        return FragmentAutoInitResult.failed;\n                    }\n                    fragments.add(fragment);\n                } catch (ClassNotFoundException e) {\n                    QMUILog.d(TAG, \"Can not find \" + fragmentClassName);\n                }\n            }\n            if (fragments.size() > 0) {\n                initMutiFragment(fragments);\n                return FragmentAutoInitResult.success;\n            }\n        }\n        return FragmentAutoInitResult.unHandled;\n    }\n\n    protected boolean initMutiFragment(QMUIFragment... fragments) {\n        List<QMUIFragment> list = new ArrayList<>(fragments.length);\n        Collections.addAll(list, fragments);\n        return initMutiFragment(list);\n    }\n\n    protected boolean initMutiFragment(List<QMUIFragment> fragments) {\n        if (fragments.size() == 0) {\n            return false;\n        }\n        boolean disableSwipeBack = getIntent().getIntExtra(QMUIFragmentActivity.QMUI_MUTI_START_INDEX, 0) > 0;\n        if (fragments.size() == 1) {\n            QMUIFragment fragment = fragments.get(0);\n            if (disableSwipeBack) {\n                fragment.mDisableSwipeBackByMutiStarted = true;\n            }\n            String tagName = fragment.getClass().getSimpleName();\n            getSupportFragmentManager()\n                    .beginTransaction()\n                    .add(getContextViewId(), fragment, tagName)\n                    .addToBackStack(tagName)\n                    .commit();\n            return true;\n        }\n        ArrayList<FragmentTransaction> transactions = new ArrayList<>();\n        FragmentManager fragmentManager = getSupportFragmentManager();\n        for (int i = 0; i < fragments.size(); i++) {\n            QMUIFragment fragment = fragments.get(i);\n            if (disableSwipeBack) {\n                fragment.mDisableSwipeBackByMutiStarted = true;\n            }\n            disableSwipeBack = true;\n            FragmentTransaction transaction = fragmentManager.beginTransaction();\n            fragment.mDisableSwipeBackByMutiStarted = true;\n            String tagName = fragment.getClass().getSimpleName();\n            if (i == 0) {\n                transaction.add(getContextViewId(), fragment, tagName);\n            } else {\n                QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n                transaction.setCustomAnimations(0, 0, transitionConfig.popenter, transitionConfig.popout);\n                transaction.replace(getContextViewId(), fragment, tagName);\n            }\n            transaction.addToBackStack(tagName);\n            transaction.setReorderingAllowed(true);\n            transactions.add(transaction);\n        }\n        for (FragmentTransaction transaction : transactions) {\n            transaction.commit();\n        }\n        return true;\n    }\n\n    protected void performTranslucent() {\n        QMUIStatusBarHelper.translucent(this);\n    }\n\n    /**\n     * used for subclasses to see if the parent class initializes the first fragment。\n     * it must be called after super.onCreate in subclasses.\n     *\n     * @return true if first fragment is initialized.\n     */\n    protected FragmentAutoInitResult isFragmentAutoInitResult() {\n        return mFragmentAutoInitResult;\n    }\n\n    protected void setFragmentAutoInitResult(FragmentAutoInitResult fragmentAutoInitResult) {\n        mFragmentAutoInitResult = fragmentAutoInitResult;\n    }\n\n    protected Class<? extends QMUIFragment> getDefaultFirstFragment() {\n        Class<?> cls = getClass();\n        while (cls != null && cls != QMUIFragmentActivity.class && QMUIFragmentActivity.class.isAssignableFrom(cls)) {\n            if (cls.isAnnotationPresent(DefaultFirstFragment.class)) {\n                DefaultFirstFragment defaultFirstFragment = cls.getAnnotation(DefaultFirstFragment.class);\n                if (defaultFirstFragment != null) {\n                    return defaultFirstFragment.value();\n                }\n            }\n            cls = cls.getSuperclass();\n        }\n        return null;\n    }\n\n    protected QMUIFragment instantiationFragment(Class<? extends QMUIFragment> cls, Bundle args) {\n        try {\n            QMUIFragment fragment = cls.newInstance();\n            if (args != null) {\n                fragment.setArguments(args);\n            }\n            return fragment;\n        } catch (IllegalAccessException e) {\n            QMUILog.d(TAG, \"Can not access \" + cls.getName() + \" for first fragment\");\n        } catch (InstantiationException e) {\n            QMUILog.d(TAG, \"Can not instance \" + cls.getName() + \" for first fragment\");\n        }\n        return null;\n    }\n\n    @Override\n    public FragmentContainerView getFragmentContainerView() {\n        return mRootView.getFragmentContainerView();\n    }\n\n    @Override\n    public ViewModelStoreOwner getContainerViewModelStoreOwner() {\n        return this;\n    }\n\n    @Override\n    public void requestForHandlePopBack(boolean toHandle) {\n        isChildHandlePopBackRequested = toHandle;\n    }\n\n    @Override\n    public boolean isChildHandlePopBackRequested() {\n        return isChildHandlePopBackRequested;\n    }\n\n    protected RootView onCreateRootView(int fragmentContainerId) {\n        return new DefaultRootView(this, fragmentContainerId);\n    }\n\n    /**\n     * get the current Fragment.\n     */\n    @Nullable\n    public Fragment getCurrentFragment() {\n        return getSupportFragmentManager().findFragmentById(getContextViewId());\n    }\n\n    @Nullable\n    private QMUIFragment getCurrentQMUIFragment() {\n        Fragment current = getCurrentFragment();\n        if (current instanceof QMUIFragment) {\n            return (QMUIFragment) current;\n        }\n        return null;\n    }\n\n\n    /**\n     * start a new fragment and then destroy current fragment.\n     * assume there is a fragment stack(A->B->C), and you use this method to start a new\n     * fragment D and destroy fragment C. Now you are in fragment D, if you want call\n     * {@link #popBackStack()} to back to B, what the animation should be? Sometimes we hope run\n     * animation generated by transition B->C, but sometimes we hope run animation generated by\n     * transition C->D. this why second parameter exists.\n     *\n     * @param fragment                      new fragment to start\n     * @param useNewTransitionConfigWhenPop if true, use animation generated by transition C->D,\n     *                                      else, use animation generated by transition B->C\n     */\n\n    public int startFragmentAndDestroyCurrent(QMUIFragment fragment, final boolean useNewTransitionConfigWhenPop) {\n        FragmentManager fragmentManager = getSupportFragmentManager();\n        if (fragmentManager.isDestroyed()) {\n            return -1;\n        }\n        if (fragmentManager.isStateSaved()) {\n            QMUILog.d(TAG, \"startFragment can not be invoked after onSaveInstanceState\");\n            return -1;\n        }\n        QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n        String tagName = fragment.getClass().getSimpleName();\n        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction()\n                .setCustomAnimations(transitionConfig.enter, transitionConfig.exit,\n                        transitionConfig.popenter, transitionConfig.popout)\n                .setPrimaryNavigationFragment(null)\n                .replace(getContextViewId(), fragment, tagName);\n        int index = transaction.commit();\n        Utils.modifyOpForStartFragmentAndDestroyCurrent(fragmentManager, fragment, useNewTransitionConfigWhenPop, transitionConfig);\n        return index;\n    }\n\n    /**\n     *\n     * @param fragment target fragment to start\n     * @return commit id\n     *\n     */\n    public int startFragment(QMUIFragment fragment) {\n        Log.i(TAG, \"startFragment\");\n        FragmentManager fragmentManager = getSupportFragmentManager();\n        if (fragmentManager.isDestroyed()) {\n            return -1;\n        }\n        if (fragmentManager.isStateSaved()) {\n            QMUILog.d(TAG, \"startFragment can not be invoked after onSaveInstanceState\");\n            return -1;\n        }\n        QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n        String tagName = fragment.getClass().getSimpleName();\n        return fragmentManager.beginTransaction()\n                .setCustomAnimations(transitionConfig.enter, transitionConfig.exit, transitionConfig.popenter, transitionConfig.popout)\n                .replace(getContextViewId(), fragment, tagName)\n                .setPrimaryNavigationFragment(null)\n                .addToBackStack(tagName)\n                .commit();\n    }\n\n\n    public int startFragments(List<QMUIFragment> fragments) {\n        Log.i(TAG, \"startFragment\");\n        FragmentManager fragmentManager = getSupportFragmentManager();\n        if (fragmentManager.isDestroyed()) {\n            return -1;\n        }\n        if (fragmentManager.isStateSaved()) {\n            QMUILog.d(TAG, \"startFragment can not be invoked after onSaveInstanceState\");\n            return -1;\n        }\n        if (fragments.size() == 0) {\n            return -1;\n        }\n        ArrayList<FragmentTransaction> transactions = new ArrayList<>();\n        QMUIFragment.TransitionConfig lastTransitionConfig = fragments.get(fragments.size() - 1).onFetchTransitionConfig();\n        for (QMUIFragment fragment : fragments) {\n            FragmentTransaction transaction = fragmentManager.beginTransaction().setPrimaryNavigationFragment(null);\n            QMUIFragment.TransitionConfig transitionConfig = fragment.onFetchTransitionConfig();\n            fragment.mDisableSwipeBackByMutiStarted = true;\n            String tagName = fragment.getClass().getSimpleName();\n            transaction.setCustomAnimations(transitionConfig.enter, lastTransitionConfig.exit, transitionConfig.popenter, transitionConfig.popout);\n            transaction.replace(getContextViewId(), fragment, tagName);\n            transaction.addToBackStack(tagName);\n            transactions.add(transaction);\n            transaction.setReorderingAllowed(true);\n        }\n        for (FragmentTransaction transaction : transactions) {\n            transaction.commit();\n        }\n        return 0;\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        QMUIFragment fragment = getCurrentQMUIFragment();\n        if (fragment != null && !fragment.isInSwipeBack() && fragment.onKeyDown(keyCode, event)) {\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n\n    @Override\n    public boolean onKeyUp(int keyCode, KeyEvent event) {\n        QMUIFragment fragment = getCurrentQMUIFragment();\n        if (fragment != null && !fragment.isInSwipeBack() && fragment.onKeyUp(keyCode, event)) {\n            return true;\n        }\n        return super.onKeyUp(keyCode, event);\n    }\n\n    public void popBackStack() {\n        getOnBackPressedDispatcher().onBackPressed();\n    }\n\n\n    public static Intent intentOf(@NonNull Context context,\n                                  @NonNull Class<? extends QMUIFragmentActivity> targetActivity,\n                                  @NonNull Class<? extends QMUIFragment> firstFragment) {\n        return intentOf(context, targetActivity, firstFragment, null);\n    }\n\n    /**\n     * create a intent for a new QMUIFragmentActivity\n     *\n     * @param context        Generally it is activity\n     * @param targetActivity target activity class\n     * @param firstFragment  first fragment in target activity\n     * @param fragmentArgs   args for first fragment\n     * @return\n     */\n    public static Intent intentOf(@NonNull Context context,\n                                  @NonNull Class<? extends QMUIFragmentActivity> targetActivity,\n                                  @NonNull Class<? extends QMUIFragment> firstFragment,\n                                  @Nullable Bundle fragmentArgs) {\n        Intent intent = new Intent(context, targetActivity);\n        intent.putExtra(QMUI_INTENT_DST_FRAGMENT_NAME, firstFragment.getName());\n        if (fragmentArgs != null) {\n            intent.putExtra(QMUI_INTENT_FRAGMENT_ARG, fragmentArgs);\n        }\n        return intent;\n    }\n\n    public static Intent intentOf(@NonNull Context context,\n                                  @NonNull Class<? extends QMUIFragmentActivity> targetActivity,\n                                  @NonNull String firstFragmentClassName,\n                                  @Nullable Bundle fragmentArgs) {\n        Intent intent = new Intent(context, targetActivity);\n        intent.putExtra(QMUI_INTENT_DST_FRAGMENT_NAME, firstFragmentClassName);\n        if (fragmentArgs != null) {\n            intent.putExtra(QMUI_INTENT_FRAGMENT_ARG, fragmentArgs);\n        }\n        return intent;\n    }\n\n    public static abstract class RootView extends FrameLayout {\n\n\n        public RootView(Context context, int fragmentContainerId) {\n            super(context);\n            setId(R.id.qmui_activity_root_id);\n        }\n\n        public abstract FragmentContainerView getFragmentContainerView();\n    }\n\n    @Override\n    public void onBackPressed() {\n        try {\n            super.onBackPressed();\n        } catch (Exception ignore) {\n            // 1. Under Android O, Activity#onBackPressed doesn't check FragmentManager's save state.\n            // 2. IndexOutOfBoundsException caused by ViewGroup#removeView(View) in EmotionUI.\n        }\n    }\n\n    @SuppressLint(\"ViewConstructor\")\n    public static class DefaultRootView extends RootView {\n        private FragmentContainerView mFragmentContainerView;\n\n        public DefaultRootView(Context context, int fragmentContainerId) {\n            super(context, fragmentContainerId);\n            mFragmentContainerView = new FragmentContainerView(context);\n            mFragmentContainerView.setId(fragmentContainerId);\n            addView(mFragmentContainerView, new FrameLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT,\n                    ViewGroup.LayoutParams.MATCH_PARENT));\n        }\n\n        @Override\n        public FragmentContainerView getFragmentContainerView() {\n            return mFragmentContainerView;\n        }\n    }\n\n    public enum FragmentAutoInitResult {success, failed, unHandled}\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragmentContainerProvider.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentContainerView;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.lifecycle.ViewModelStoreOwner;\n\npublic interface QMUIFragmentContainerProvider {\n    int getContextViewId();\n\n    FragmentManager getContainerFragmentManager();\n\n    @Nullable\n    FragmentContainerView getFragmentContainerView();\n\n    ViewModelStoreOwner getContainerViewModelStoreOwner();\n\n    void requestForHandlePopBack(boolean toHandle);\n\n    boolean isChildHandlePopBackRequested();\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragmentPagerAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.annotation.SuppressLint;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.lifecycle.Lifecycle;\n\nimport com.qmuiteam.qmui.widget.QMUIPagerAdapter;\n\npublic abstract class QMUIFragmentPagerAdapter extends QMUIPagerAdapter {\n\n    private final FragmentManager mFragmentManager;\n    private FragmentTransaction mCurrentTransaction;\n    private Fragment mCurrentPrimaryItem = null;\n\n    public QMUIFragmentPagerAdapter(@NonNull FragmentManager fm) {\n        mFragmentManager = fm;\n    }\n\n    public abstract QMUIFragment createFragment(int position);\n\n    @Override\n    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {\n        return view == ((Fragment) object).getView();\n    }\n\n    @SuppressLint(\"CommitTransaction\")\n    @Override\n    @NonNull\n    protected Object hydrate(@NonNull ViewGroup container, int position) {\n        String name = makeFragmentName(container.getId(), position);\n        if (mCurrentTransaction == null) {\n            mCurrentTransaction = mFragmentManager.beginTransaction();\n        }\n        Fragment fragment = mFragmentManager.findFragmentByTag(name);\n        if (fragment != null) {\n            return fragment;\n        }\n        return createFragment(position);\n    }\n\n    @SuppressLint(\"CommitTransaction\")\n    @Override\n    protected void populate(@NonNull ViewGroup container, @NonNull Object item, int position) {\n        String name = makeFragmentName(container.getId(), position);\n        if (mCurrentTransaction == null) {\n            mCurrentTransaction = mFragmentManager.beginTransaction();\n        }\n        Fragment fragment = mFragmentManager.findFragmentByTag(name);\n        if (fragment != null) {\n            mCurrentTransaction.attach(fragment);\n            if (fragment.getView() != null && fragment.getView().getWidth() == 0) {\n                fragment.getView().requestLayout();\n            }\n        } else {\n            fragment = (Fragment) item;\n            mCurrentTransaction.add(container.getId(), fragment, name);\n        }\n        if (fragment != mCurrentPrimaryItem) {\n            fragment.setMenuVisibility(false);\n            mCurrentTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);\n        }\n    }\n\n    @SuppressLint(\"CommitTransaction\")\n    @Override\n    protected void destroy(@NonNull ViewGroup container, int position, @NonNull Object object) {\n        Fragment fragment = (Fragment) object;\n        if (mCurrentTransaction == null) {\n            mCurrentTransaction = mFragmentManager.beginTransaction();\n        }\n        mCurrentTransaction.detach(fragment);\n        if (fragment == mCurrentPrimaryItem) {\n            mCurrentPrimaryItem = null;\n        }\n    }\n\n    @Override\n    public void startUpdate(@NonNull ViewGroup container) {\n        if (container.getId() == View.NO_ID) {\n            throw new IllegalStateException(\"ViewPager with adapter \" + this\n                    + \" requires a view id\");\n        }\n    }\n\n    @Override\n    public void finishUpdate(@NonNull ViewGroup container) {\n        if (mCurrentTransaction != null) {\n            mCurrentTransaction.commitNowAllowingStateLoss();\n            mCurrentTransaction = null;\n        }\n    }\n\n    @Override\n    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {\n        Fragment fragment = (Fragment) object;\n        if (fragment != mCurrentPrimaryItem) {\n            if (mCurrentPrimaryItem != null) {\n                mCurrentPrimaryItem.setMenuVisibility(false);\n                if (mCurrentTransaction == null) {\n                    mCurrentTransaction = mFragmentManager.beginTransaction();\n                }\n                mCurrentTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);\n            }\n            fragment.setMenuVisibility(true);\n            if (mCurrentTransaction == null) {\n                mCurrentTransaction = mFragmentManager.beginTransaction();\n            }\n            mCurrentTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);\n            mCurrentPrimaryItem = fragment;\n        }\n    }\n\n    private String makeFragmentName(int viewId, long id) {\n        return \"QMUIFragmentPagerAdapter:\" + viewId + \":\" + id;\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUILatestVisit.java",
    "content": "package com.qmuiteam.qmui.arch;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport androidx.annotation.MainThread;\nimport androidx.fragment.app.Fragment;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.arch.record.DefaultLatestVisitStorage;\nimport com.qmuiteam.qmui.arch.record.QMUILatestVisitStorage;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditorImpl;\nimport com.qmuiteam.qmui.arch.record.RecordIdClassMap;\n\nimport java.util.Map;\n\npublic class QMUILatestVisit {\n    private static final String TAG = \"QMUILatestVisit\";\n    private static String NAV_STORE_PREFIX = \"_qmui_nav\";\n    private static String NAV_STORE_FRAGMENT_SUFFIX = \".class\";\n    private static QMUILatestVisit sInstance;\n    private QMUILatestVisitStorage mStorage;\n    private Context mContext;\n    private RecordIdClassMap mRecordMap;\n    private RecordArgumentEditor mRecordArgumentEditor;\n    private RecordArgumentEditor mNavRecordArgumentEditor;\n\n    private QMUILatestVisit(Context context) {\n        mContext = context.getApplicationContext();\n        mRecordArgumentEditor = new RecordArgumentEditorImpl();\n        mNavRecordArgumentEditor = new RecordArgumentEditorImpl();\n        try {\n            Class<?> cls = Class.forName(RecordIdClassMap.class.getCanonicalName() + \"Impl\");\n            mRecordMap = (RecordIdClassMap) cls.newInstance();\n        } catch (ClassNotFoundException e) {\n            mRecordMap = new RecordIdClassMap() {\n                @Override\n                public Class<?> getRecordClassById(int id) {\n                    return null;\n                }\n\n                @Override\n                public int getIdByRecordClass(Class<?> clazz) {\n                    return QMUILatestVisitStorage.NOT_EXIST;\n                }\n            };\n        } catch (IllegalAccessException e) {\n            throw new RuntimeException(\"Can not access the Class RecordMetaMapImpl. \" +\n                    \"Please file a issue to report this.\");\n        } catch (InstantiationException e) {\n            throw new RuntimeException(\"Can not instance the Class RecordMetaMapImpl. \" +\n                    \"Please file a issue to report this.\");\n        }\n    }\n\n    @MainThread\n    public static QMUILatestVisit getInstance(Context context) {\n        if (sInstance == null) {\n            sInstance = new QMUILatestVisit(context);\n        }\n        return sInstance;\n    }\n\n    public static Intent intentOfLatestVisit(Activity activity) {\n        return getInstance(activity).getLatestVisitIntent(activity);\n    }\n\n    public void setStorage(QMUILatestVisitStorage storage) {\n        mStorage = storage;\n    }\n\n    QMUILatestVisitStorage getStorage() {\n        if (mStorage == null) {\n            mStorage = new DefaultLatestVisitStorage(mContext);\n        }\n        return mStorage;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Intent getLatestVisitIntent(Context context) {\n        int activityId = getStorage().getActivityRecordId();\n        if (activityId == QMUILatestVisitStorage.NOT_EXIST) {\n            return null;\n        }\n        Class<?> activityCls = mRecordMap.getRecordClassById(activityId);\n        if (activityCls == null) {\n            return null;\n        }\n        Intent intent;\n        try {\n            if (QMUIFragmentActivity.class.isAssignableFrom(activityCls)) {\n                int fragmentId = getStorage().getFragmentRecordId();\n                if (fragmentId == QMUILatestVisitStorage.NOT_EXIST) {\n                    return null;\n                }\n                Class<?> fragmentCls = mRecordMap.getRecordClassById(fragmentId);\n                if (fragmentCls == null) {\n                    return null;\n                }\n                Class<? extends QMUIFragmentActivity> activity = (Class<? extends QMUIFragmentActivity>) activityCls;\n                Class<? extends QMUIFragment> fragment = (Class<? extends QMUIFragment>) fragmentCls;\n                Map<String, RecordArgumentEditor.Argument> arguments = getStorage().getFragmentArguments();\n                if (arguments == null || arguments.isEmpty()) {\n                    intent = QMUIFragmentActivity.intentOf(context, activity, fragment, null);\n                } else {\n                    Bundle bundle = new Bundle();\n                    boolean hasNav = false;\n                    for (String key : arguments.keySet()) {\n                        if (key.startsWith(NAV_STORE_PREFIX)) {\n                            hasNav = true;\n                        } else {\n                            RecordArgumentEditor.Argument argument = arguments.get(key);\n                            if (argument != null) {\n                                argument.putToBundle(bundle, key);\n                            }\n                        }\n                    }\n                    if (!hasNav) {\n                        intent = QMUIFragmentActivity.intentOf(context, activity, fragment, bundle);\n                    } else {\n                        int navLevel = 0;\n                        String fragmentClassName = fragment.getName();\n                        while (true) {\n                            String navPrefix = getNavFragmentStorePrefix(navLevel);\n                            String navClassNameKey = navPrefix + NAV_STORE_FRAGMENT_SUFFIX;\n                            RecordArgumentEditor.Argument navClassNameArg = arguments.get(navClassNameKey);\n                            if (navClassNameArg == null) {\n                                break;\n                            }\n                            bundle = QMUINavFragment.initArguments(fragmentClassName, bundle);\n                            fragmentClassName = (String) navClassNameArg.getValue();\n                            for (String key : arguments.keySet()) {\n                                if (key.startsWith(navPrefix) && !key.equals(navClassNameKey)) {\n                                    RecordArgumentEditor.Argument arg = arguments.get(key);\n                                    if (arg != null) {\n                                        arg.putToBundle(bundle, key.substring(navPrefix.length()));\n                                    }\n                                }\n                            }\n                            navLevel++;\n                        }\n                        intent = QMUIFragmentActivity.intentOf(context, activity, fragmentClassName, bundle);\n                    }\n                }\n            } else {\n                intent = new Intent(context, activityCls);\n            }\n            getStorage().getAndWriteActivityArgumentsToIntent(intent);\n            return intent;\n        } catch (Throwable throwable) {\n            QMUILog.e(TAG, \"getLatestVisitIntent failed.\", throwable);\n            getStorage().clearAll();\n        }\n        return null;\n    }\n\n\n    void clearFragmentLatestVisitRecord() {\n        getStorage().clearFragmentStorage();\n    }\n\n    void clearActivityLatestVisitRecord() {\n        getStorage().clearActivityStorage();\n    }\n\n    void performLatestVisitRecord(QMUIFragment fragment) {\n        int id = mRecordMap.getIdByRecordClass(fragment.getClass());\n        if (id == QMUILatestVisitStorage.NOT_EXIST) {\n            return;\n        }\n        mRecordArgumentEditor.clear();\n        mNavRecordArgumentEditor.clear();\n        fragment.onCollectLatestVisitArgument(mRecordArgumentEditor);\n        Fragment parent = fragment.getParentFragment();\n        int level = 0;\n        while (parent instanceof QMUINavFragment) {\n            String navInfo = getNavFragmentStorePrefix(level);\n            QMUINavFragment nav = (QMUINavFragment) parent;\n            mNavRecordArgumentEditor.clear();\n            nav.onCollectLatestVisitArgument(mNavRecordArgumentEditor);\n            Map<String, RecordArgumentEditor.Argument> args = mNavRecordArgumentEditor.getAll();\n            mRecordArgumentEditor.putString(navInfo + NAV_STORE_FRAGMENT_SUFFIX, nav.getClass().getName());\n            for (String arg : args.keySet()) {\n                mRecordArgumentEditor.put(navInfo + arg, args.get(arg));\n            }\n            parent = parent.getParentFragment();\n            level++;\n        }\n        getStorage().saveFragmentRecordInfo(id, mRecordArgumentEditor.getAll());\n        mRecordArgumentEditor.clear();\n        mNavRecordArgumentEditor.clear();\n    }\n\n    void performLatestVisitRecord(InnerBaseActivity activity) {\n        int id = mRecordMap.getIdByRecordClass(activity.getClass());\n        if (id == QMUILatestVisitStorage.NOT_EXIST) {\n            return;\n        }\n        mRecordArgumentEditor.clear();\n        activity.onCollectLatestVisitArgument(mRecordArgumentEditor);\n        getStorage().saveActivityRecordInfo(id, mRecordArgumentEditor.getAll());\n        mRecordArgumentEditor.clear();\n    }\n\n\n    private String getNavFragmentStorePrefix(int level) {\n        return NAV_STORE_PREFIX + level + \"_\";\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUINavFragment.java",
    "content": "package com.qmuiteam.qmui.arch;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentContainerView;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.lifecycle.Lifecycle;\nimport androidx.lifecycle.ViewModelStoreOwner;\n\nimport com.qmuiteam.qmui.QMUILog;\n\npublic class QMUINavFragment extends QMUIFragment implements QMUIFragmentContainerProvider {\n    private static final String TAG = \"QMUINavFragment\";\n    private static final String QMUI_ARGUMENT_DST_FRAGMENT = \"qmui_argument_dst_fragment\";\n    private static final String QMUI_ARGUMENT_FRAGMENT_ARG = \"qmui_argument_fragment_arg\";\n    private FragmentContainerView mContainerView;\n    private boolean mIsFirstFragmentAdded = false;\n    private boolean isChildHandlePopBackRequested = false;\n\n    public static QMUINavFragment getDefaultInstance(Class<? extends QMUIFragment> firstFragmentCls,\n                                                     @Nullable Bundle firstFragmentArgument){\n        QMUINavFragment navFragment = new QMUINavFragment();\n        Bundle arg = new Bundle();\n        arg.putString(QMUI_ARGUMENT_DST_FRAGMENT, firstFragmentCls.getName());\n        arg.putBundle(QMUI_ARGUMENT_FRAGMENT_ARG, firstFragmentArgument);\n        navFragment.setArguments(initArguments(firstFragmentCls, firstFragmentArgument));\n        return navFragment;\n    }\n\n    public static Bundle initArguments(Class<? extends QMUIFragment> firstFragmentCls,\n                                           @Nullable Bundle firstFragmentArgument){\n        Bundle arg = new Bundle();\n        arg.putString(QMUI_ARGUMENT_DST_FRAGMENT, firstFragmentCls.getName());\n        arg.putBundle(QMUI_ARGUMENT_FRAGMENT_ARG, firstFragmentArgument);\n        return arg;\n    }\n\n    static Bundle initArguments(String firstFragmentClsName, @Nullable Bundle firstFragmentArgument){\n        Bundle arg = new Bundle();\n        arg.putString(QMUI_ARGUMENT_DST_FRAGMENT, firstFragmentClsName);\n        arg.putBundle(QMUI_ARGUMENT_FRAGMENT_ARG, firstFragmentArgument);\n        return arg;\n    }\n\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if (savedInstanceState == null) {\n            onCreateFirstFragment();\n        }\n    }\n\n    public boolean isFirstFragmentAdded() {\n        return mIsFirstFragmentAdded;\n    }\n\n    protected void setFirstFragmentAdded(boolean firstFragmentAdded) {\n        mIsFirstFragmentAdded = firstFragmentAdded;\n    }\n\n    protected void onCreateFirstFragment(){\n        Bundle arguments = getArguments();\n        if (arguments != null) {\n            String dstFragmentName = arguments.getString(QMUI_ARGUMENT_DST_FRAGMENT);\n            QMUIFragment firstFragment = instantiationFirstFragment(dstFragmentName, arguments);\n            if (firstFragment != null) {\n                mIsFirstFragmentAdded = true;\n                getChildFragmentManager()\n                        .beginTransaction()\n                        .add(getContextViewId(), firstFragment, firstFragment.getClass().getSimpleName())\n                        .addToBackStack(firstFragment.getClass().getSimpleName())\n                        .commit();\n            }\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    private QMUIFragment instantiationFirstFragment(String clsName, Bundle arguments) {\n        try {\n            Class<? extends QMUIFragment> cls = (Class<? extends QMUIFragment>) Class.forName(clsName);\n            QMUIFragment fragment = cls.newInstance();\n            Bundle args = arguments.getBundle(QMUI_ARGUMENT_FRAGMENT_ARG);\n            if (args != null) {\n                fragment.setArguments(args);\n            }\n            return fragment;\n        } catch (IllegalAccessException e) {\n            QMUILog.d(TAG, \"Can not access \" + clsName + \" for first fragment\");\n        } catch (java.lang.InstantiationException e) {\n            QMUILog.d(TAG, \"Can not instance \" + clsName + \" for first fragment\");\n        } catch (ClassNotFoundException e) {\n            QMUILog.d(TAG, \"Can not find \" + clsName);\n        }\n        return null;\n    }\n\n    @Override\n    protected View onCreateView() {\n        FragmentContainerView rootView = new FragmentContainerView(getContext());\n        rootView.setId(getContextViewId());\n        return rootView;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        mContainerView = view.findViewById(getContextViewId());\n        if(mContainerView == null){\n            throw new RuntimeException(\"must call #configFragmentContainerView() in onCreateView()\");\n        }\n    }\n\n    protected void configFragmentContainerView(FragmentContainerView fragmentContainerView){\n        fragmentContainerView.setId(getContextViewId());\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        mContainerView = null;\n    }\n\n    @Override\n    public int getContextViewId() {\n        return R.id.qmui_activity_fragment_container_id;\n    }\n\n    @Override\n    public void requestForHandlePopBack(boolean toHandle) {\n        isChildHandlePopBackRequested = toHandle;\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n        if(provider != null){\n            provider.requestForHandlePopBack(toHandle || getChildFragmentManager().getBackStackEntryCount() > 1);\n        }\n    }\n\n    @Override\n    public boolean isChildHandlePopBackRequested() {\n        return isChildHandlePopBackRequested;\n    }\n\n    @Override\n    public void onAttach(@NonNull Context context) {\n        super.onAttach(context);\n        getChildFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {\n            @Override\n            public void onBackStackChanged() {\n                checkForRequestForHandlePopBack();\n                if(getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)){\n                    checkForPrimaryNavigation();\n                }\n            }\n        });\n    }\n\n    private void checkForPrimaryNavigation(){\n        getParentFragmentManager()\n                .beginTransaction()\n                .setPrimaryNavigationFragment(getChildFragmentManager().getBackStackEntryCount() > 1 ? QMUINavFragment.this : null)\n                .commit();\n    }\n\n    @Override\n    protected void checkForRequestForHandlePopBack(){\n        boolean enoughBackStackCount = getChildFragmentManager().getBackStackEntryCount() > 1;\n        QMUIFragmentContainerProvider provider = findFragmentContainerProvider(false);\n        if(provider != null){\n            provider.requestForHandlePopBack(isChildHandlePopBackRequested || enoughBackStackCount);\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        checkForPrimaryNavigation();\n    }\n\n    @Override\n    public FragmentManager getContainerFragmentManager() {\n        return getChildFragmentManager();\n    }\n\n    @Override\n    public ViewModelStoreOwner getContainerViewModelStoreOwner() {\n        return this;\n    }\n\n    @Nullable\n    @Override\n    public FragmentContainerView getFragmentContainerView() {\n        return mContainerView;\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/QMUISwipeBackActivityManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Bundle;\nimport androidx.annotation.MainThread;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.Stack;\n\n\npublic class QMUISwipeBackActivityManager implements Application.ActivityLifecycleCallbacks {\n    private static QMUISwipeBackActivityManager sInstance;\n    private Stack<Activity> mActivityStack = new Stack<>();\n    private Activity mCurrentActivity = null;\n\n\n    @MainThread\n    public static QMUISwipeBackActivityManager getInstance() {\n        if (sInstance == null) {\n            throw new IllegalAccessError(\"the QMUISwipeBackActivityManager is not initialized; \" +\n                    \"please call QMUISwipeBackActivityManager.init(Application) in your application.\");\n        }\n        return sInstance;\n    }\n\n    private QMUISwipeBackActivityManager() {\n    }\n\n    public static void init(@NonNull Application application) {\n        if (sInstance == null) {\n            sInstance = new QMUISwipeBackActivityManager();\n            application.registerActivityLifecycleCallbacks(sInstance);\n        }\n    }\n\n    @Override\n    public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {\n        if(mCurrentActivity == null){\n            mCurrentActivity = activity;\n        }\n        mActivityStack.add(activity);\n    }\n\n    @Override\n    public void onActivityDestroyed(@NonNull Activity activity) {\n        mActivityStack.remove(activity);\n        if(mActivityStack.isEmpty()){\n            mCurrentActivity = null;\n        }\n    }\n\n    @Override\n    public void onActivityStarted(@NonNull Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityResumed(@NonNull Activity activity) {\n        mCurrentActivity = activity;\n    }\n\n    @Override\n    public void onActivityPaused(@NonNull Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityStopped(@NonNull Activity activity) {\n\n    }\n\n    @Override\n    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {\n\n    }\n\n    @Nullable\n    public Activity getCurrentActivity(){\n        return mCurrentActivity;\n    }\n\n    public int getActivityCount(){\n        return mActivityStack.size();\n    }\n\n    @Nullable\n    public Activity getActivityInStack(int index){\n        if(index < 0 || index >= mActivityStack.size()){\n            return null;\n        }\n        return mActivityStack.get(index);\n    }\n\n    /**\n     *\n     * refer to https://github.com/bingoogolapple/BGASwipeBackLayout-Android/\n     * @param currentActivity the last activity\n     * @return\n     */\n    @Nullable\n    public Activity getPenultimateActivity(Activity currentActivity) {\n        Activity activity = null;\n        try {\n            if (mActivityStack.size() > 1) {\n                activity = mActivityStack.get(mActivityStack.size() - 2);\n\n                if (currentActivity.equals(activity)) {\n                    int index = mActivityStack.indexOf(currentActivity);\n                    if (index > 0) {\n                        // if memory leaks or the last activity is being finished\n                        activity = mActivityStack.get(index - 1);\n                    } else if (mActivityStack.size() == 2) {\n                        // if screen orientation changes, there may be an error sequence in the stack\n                        activity = mActivityStack.lastElement();\n                    }\n                }\n            }\n        } catch (Exception ignored) {\n        }\n        return activity;\n    }\n\n    public boolean canSwipeBack(Activity currentActivity) {\n        if(currentActivity == null){\n            return false;\n        }\n        Activity prevActivity = getPenultimateActivity(currentActivity);\n        return prevActivity != null && !prevActivity.isDestroyed() && !prevActivity.isFinishing();\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/SwipeBackLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.Drawable;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.FrameLayout;\nimport android.widget.OverScroller;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.qmuiteam.qmui.QMUIInterpolatorStaticHolder.QUNITIC_INTERPOLATOR;\n\n/**\n * Created by cgspine on 2018/1/7.\n * <p>\n * modified from https://github.com/ikew0ng/SwipeBackLayout\n */\n\n\npublic class SwipeBackLayout extends FrameLayout {\n\n    private static final int MIN_FLING_VELOCITY = 400; // dips per second\n    private static final int DEFAULT_SCRIM_COLOR = 0x99000000;\n    private static final int FULL_ALPHA = 255;\n    private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f;\n    private static final int BASE_SETTLE_DURATION = 256; // ms\n    private static final int MAX_SETTLE_DURATION = 600; // ms\n\n\n    public static final int DRAG_DIRECTION_NONE = 0;\n    public static final int DRAG_DIRECTION_LEFT_TO_RIGHT = 1;\n    public static final int DRAG_DIRECTION_RIGHT_TO_LEFT = 2;\n    public static final int DRAG_DIRECTION_TOP_TO_BOTTOM = 3;\n    public static final int DRAG_DIRECTION_BOTTOM_TO_TOP = 4;\n\n    public static final int EDGE_LEFT = 1;\n    public static final int EDGE_RIGHT = 2;\n    public static final int EDGE_TOP = 4;\n    public static final int EDGE_BOTTOM = 8;\n\n\n    public static final int STATE_IDLE = 0;\n    public static final int STATE_DRAGGING = 1;\n    public static final int STATE_SETTLING = 2;\n\n    public static final ViewMoveAction MOVE_VIEW_AUTO = new ViewMoveAuto();\n    public static final ViewMoveAction MOVE_VIEW_LEFT_TO_RIGHT = new ViewMoveLeftToRight();\n    public static final ViewMoveAction MOVE_VIEW_TOP_TO_BOTTOM = new ViewMoveTopToBottom();\n\n    private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD;\n\n    private View mContentView;\n    private List<SwipeListener> mListeners;\n    private Callback mCallback;\n    private OnInsetsHandler mOnInsetsHandler;\n\n    private Drawable mShadowLeft;\n    private Drawable mShadowRight;\n    private Drawable mShadowBottom;\n    private Drawable mShadowTop;\n\n    private float mScrimOpacity;\n    private int mScrimColor = DEFAULT_SCRIM_COLOR;\n    private VelocityTracker mVelocityTracker;\n    private float mMaxVelocity;\n    private float mMinVelocity;\n    private OverScroller mScroller;\n    private int mTouchSlop;\n\n    private float mInitialMotionX;\n    private float mInitialMotionY;\n    private float mLastMotionX;\n    private float mLastMotionY;\n\n    private int mDragState = STATE_IDLE;\n    private QMUIViewOffsetHelper mViewOffsetHelper;\n    private ViewMoveAction mViewMoveAction = MOVE_VIEW_AUTO;\n\n    private int mCurrentDragDirection = 0;\n    private boolean mIsScrollOverValid = true;\n    private boolean mEnableSwipeBack = true;\n\n    private int mRequestLayoutCount = 0;\n    private long mRequestLayoutCheckStartTime = -1;\n\n\n    public SwipeBackLayout(Context context) {\n        this(context, null);\n    }\n\n    public SwipeBackLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.SwipeBackLayoutStyle);\n    }\n\n    public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs);\n\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeBackLayout, defStyle,\n                R.style.SwipeBackLayout);\n\n        int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left,\n                R.drawable.shadow_left);\n        int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right,\n                R.drawable.shadow_right);\n        int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom,\n                R.drawable.shadow_bottom);\n        int shadowTop = a.getResourceId(R.styleable.SwipeBackLayout_shadow_top,\n                R.drawable.shadow_top);\n        setShadow(shadowLeft, EDGE_LEFT);\n        setShadow(shadowRight, EDGE_RIGHT);\n        setShadow(shadowBottom, EDGE_BOTTOM);\n        setShadow(shadowTop, EDGE_TOP);\n        a.recycle();\n        final float density = getResources().getDisplayMetrics().density;\n        final float minVel = MIN_FLING_VELOCITY * density;\n        final ViewConfiguration vc = ViewConfiguration.get(context);\n        mTouchSlop = vc.getScaledTouchSlop();\n        mMaxVelocity = vc.getScaledMaximumFlingVelocity();\n        mMinVelocity = minVel;\n        mScroller = new OverScroller(context, QUNITIC_INTERPOLATOR);\n        QMUIWindowInsetHelper.setOnApplyWindowInsetsListener(this, new androidx.core.view.OnApplyWindowInsetsListener() {\n            @Override\n            public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {\n                int insetsType = mOnInsetsHandler != null ? mOnInsetsHandler.getInsetsType() : 0;\n                if(insetsType != 0){\n                    Insets toUsed = insets.getInsets(insetsType);\n                    v.setPadding(toUsed.left, toUsed.top, toUsed.right, toUsed.bottom);\n                }else{\n                    v.setPadding(0, 0, 0, 0);\n                }\n                return insets;\n            }\n        }, false);\n    }\n\n    public void setEnableSwipeBack(boolean enableSwipeBack) {\n        mEnableSwipeBack = enableSwipeBack;\n    }\n\n    public boolean isEnableSwipeBack() {\n        return mEnableSwipeBack;\n    }\n\n    private final Runnable mSetIdleRunnable = new Runnable() {\n        @Override\n        public void run() {\n            setDragState(STATE_IDLE);\n        }\n    };\n\n    /**\n     * Set up contentView which will be moved by user gesture\n     *\n     * @param view\n     */\n    private void setContentView(View view) {\n        mContentView = view;\n        mViewOffsetHelper = new QMUIViewOffsetHelper(view);\n    }\n\n    public void setViewMoveAction(@NonNull ViewMoveAction viewMoveAction) {\n        mViewMoveAction = viewMoveAction;\n    }\n\n    public View getContentView() {\n        return mContentView;\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    /**\n     * Set a color to use for the scrim that obscures primary content while a\n     * drawer is open.\n     *\n     * @param color Color to use in 0xAARRGGBB format.\n     */\n    public void setScrimColor(int color) {\n        mScrimColor = color;\n        invalidate();\n    }\n\n    /**\n     * Add a callback to be invoked when a swipe event is sent to this view.\n     *\n     * @param listener the swipe listener to attach to this view\n     */\n    public ListenerRemover addSwipeListener(final SwipeListener listener) {\n        if (mListeners == null) {\n            mListeners = new ArrayList<>();\n        }\n        mListeners.add(listener);\n        return new ListenerRemover() {\n            @Override\n            public void remove() {\n                mListeners.remove(listener);\n            }\n        };\n    }\n\n    /**\n     * Removes a listener from the set of listeners\n     *\n     * @param listener\n     */\n    public void removeSwipeListener(SwipeListener listener) {\n        if (mListeners == null) {\n            return;\n        }\n        mListeners.remove(listener);\n    }\n\n    public void clearSwipeListeners() {\n        if (mListeners == null) {\n            return;\n        }\n        mListeners.clear();\n        mListeners = null;\n    }\n\n    public void setOnInsetsHandler(OnInsetsHandler insetsHandler) {\n        mOnInsetsHandler = insetsHandler;\n    }\n\n    /**\n     * Set scroll threshold, we will close the activity, when scrollPercent over\n     * this value\n     *\n     * @param threshold\n     */\n    public void setScrollThresHold(float threshold) {\n        if (threshold >= 1.0f || threshold <= 0) {\n            throw new IllegalArgumentException(\"Threshold value should be between 0 and 1.0\");\n        }\n        mScrollThreshold = threshold;\n    }\n\n    /**\n     * Set a drawable used for edge shadow.\n     *\n     * @param shadow   Drawable to use\n     * @param edgeFlag Combination of edge flags describing the edge to set\n     */\n    public void setShadow(Drawable shadow, int edgeFlag) {\n        if ((edgeFlag & EDGE_LEFT) != 0) {\n            mShadowLeft = shadow;\n        } else if ((edgeFlag & EDGE_RIGHT) != 0) {\n            mShadowRight = shadow;\n        } else if ((edgeFlag & EDGE_BOTTOM) != 0) {\n            mShadowBottom = shadow;\n        } else if ((edgeFlag & EDGE_TOP) != 0) {\n            mShadowTop = shadow;\n        }\n        invalidate();\n    }\n\n    /**\n     * Set a drawable used for edge shadow.\n     *\n     * @param resId    Resource of drawable to use\n     * @param edgeFlag Combination of edge flags describing the edge to set\n     * @see #EDGE_LEFT\n     * @see #EDGE_RIGHT\n     * @see #EDGE_BOTTOM\n     */\n    public void setShadow(int resId, int edgeFlag) {\n        setShadow(getResources().getDrawable(resId), edgeFlag);\n    }\n\n    void setDragState(int state) {\n        removeCallbacks(mSetIdleRunnable);\n        if (mDragState != state) {\n            mDragState = state;\n            onViewDragStateChanged(mDragState);\n        }\n    }\n\n    private boolean isTouchInContentView(float x, float y) {\n        return x >= mContentView.getLeft() && x < mContentView.getRight()\n                && y >= mContentView.getTop() && y < mContentView.getBottom();\n    }\n\n\n    private int selectDragDirection(float x, float y) {\n        final float dx = x - mInitialMotionX;\n        final float dy = y - mInitialMotionY;\n        mCurrentDragDirection = mCallback == null ? DRAG_DIRECTION_NONE :\n                mCallback.getDragDirection(this, mViewMoveAction, mInitialMotionX, mInitialMotionY, dx, dy, mTouchSlop);\n        if(mCurrentDragDirection != DRAG_DIRECTION_NONE){\n            mInitialMotionX = mLastMotionX = x;\n            mInitialMotionY = mLastMotionY = y;\n            onSwipeBackBegin();\n            requestParentDisallowInterceptTouchEvent(true);\n            setDragState(STATE_DRAGGING);\n        }\n        return mCurrentDragDirection;\n    }\n\n    private float getTouchMoveDelta(float x, float y) {\n        if (mCurrentDragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT ||\n                mCurrentDragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT) {\n            return x - mLastMotionX;\n        } else {\n            return y - mLastMotionY;\n        }\n    }\n\n    @Override\n    public void requestLayout() {\n        super.requestLayout();\n        mRequestLayoutCount++;\n        if(mRequestLayoutCheckStartTime == -1){\n            mRequestLayoutCheckStartTime = SystemClock.elapsedRealtime();\n        }\n        if(mRequestLayoutCount >= 100){\n            long duration = SystemClock.elapsedRealtime() - mRequestLayoutCheckStartTime;\n            if(duration < 4000){\n                if(mCallback != null){\n                    mCallback.reportFrequentlyRequestLayout(mRequestLayoutCount, duration);\n                }\n            }\n            mRequestLayoutCount = 0;\n            mRequestLayoutCheckStartTime = -1;\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mRequestLayoutCount=0;\n        mRequestLayoutCheckStartTime = -1;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if(!mEnableSwipeBack){\n            cancel();\n            return false;\n        }\n\n        final int action = ev.getActionMasked();\n        if (action == MotionEvent.ACTION_DOWN) {\n            cancel();\n        }\n\n        if (mVelocityTracker == null) {\n            mVelocityTracker = VelocityTracker.obtain();\n        }\n        mVelocityTracker.addMovement(ev);\n\n\n        final float x = ev.getX();\n        final float y = ev.getY();\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mInitialMotionX = mLastMotionX = x;\n                mInitialMotionY = mLastMotionY = y;\n                if (mDragState == STATE_SETTLING) {\n                    if (isTouchInContentView(x, y)) {\n                        requestParentDisallowInterceptTouchEvent(true);\n                        setDragState(STATE_DRAGGING);\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                if (mDragState == STATE_IDLE) {\n                    selectDragDirection(x, y);\n                } else if (mDragState == STATE_DRAGGING) {\n                    mViewMoveAction.move(this, mContentView, mViewOffsetHelper,\n                            mCurrentDragDirection, getTouchMoveDelta(x, y));\n                    onScroll();\n                } else {\n                    if (isTouchInContentView(x, y)) {\n                        requestParentDisallowInterceptTouchEvent(true);\n                        setDragState(STATE_DRAGGING);\n                    }\n                }\n                mLastMotionX = x;\n                mLastMotionY = y;\n                break;\n            }\n\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL: {\n                cancel();\n                break;\n            }\n        }\n\n        return mDragState == STATE_DRAGGING;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if(!mEnableSwipeBack){\n            cancel();\n            return false;\n        }\n\n        final int action = ev.getActionMasked();\n\n        if (action == MotionEvent.ACTION_DOWN) {\n            cancel();\n        }\n\n        if (mVelocityTracker == null) {\n            mVelocityTracker = VelocityTracker.obtain();\n        }\n        mVelocityTracker.addMovement(ev);\n        final float x = ev.getX();\n        final float y = ev.getY();\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mInitialMotionX = mLastMotionX = x;\n                mInitialMotionY = mLastMotionY = y;\n                if (mDragState == STATE_SETTLING) {\n                    if (isTouchInContentView(x, y)) {\n                        requestParentDisallowInterceptTouchEvent(true);\n                        setDragState(STATE_DRAGGING);\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                if (mDragState == STATE_IDLE) {\n                    selectDragDirection(x, y);\n                } else if (mDragState == STATE_DRAGGING) {\n                    mViewMoveAction.move(this, mContentView, mViewOffsetHelper,\n                            mCurrentDragDirection, getTouchMoveDelta(x, y));\n                    onScroll();\n                } else {\n                    if (isTouchInContentView(x, y)) {\n                        requestParentDisallowInterceptTouchEvent(true);\n                        setDragState(STATE_DRAGGING);\n                    }\n                }\n                mLastMotionX = x;\n                mLastMotionY = y;\n                break;\n            }\n\n            case MotionEvent.ACTION_UP: {\n                if (mDragState == STATE_DRAGGING) {\n                    releaseViewForPointerUp();\n                }\n                cancel();\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL: {\n                if (mDragState == STATE_DRAGGING) {\n                    settleContentViewAt(0, 0,\n                            (int) mVelocityTracker.getXVelocity(),\n                            (int) mVelocityTracker.getYVelocity());\n                }\n                cancel();\n                break;\n            }\n        }\n        return true;\n    }\n\n    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {\n        final ViewParent parent = getParent();\n        if (parent != null) {\n            parent.requestDisallowInterceptTouchEvent(disallowIntercept);\n        }\n    }\n\n    private void releaseViewForPointerUp() {\n        mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n        int moveEdge = mViewMoveAction.getEdge(mCurrentDragDirection);\n        float v;\n        if(mCurrentDragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT ||\n                mCurrentDragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT){\n            v = clampMag(mVelocityTracker.getXVelocity(), mMinVelocity, mMaxVelocity);\n        }else{\n            v = clampMag(mVelocityTracker.getYVelocity(), mMinVelocity, mMaxVelocity);\n        }\n\n        if (moveEdge == EDGE_LEFT || moveEdge == EDGE_RIGHT) {\n            int target = mViewMoveAction.getSettleTarget(this, mContentView,\n                    v, mCurrentDragDirection, mScrollThreshold);\n            settleContentViewAt(target, 0, (int) v, 0);\n        } else {\n            int target = mViewMoveAction.getSettleTarget(this, mContentView,\n                    v, mCurrentDragDirection, mScrollThreshold);\n            settleContentViewAt(0, target, 0, (int) v);\n        }\n    }\n\n    /**\n     * Settle the captured view at the given (left, top) position.\n     *\n     * @param finalLeft Target left position for the captured view\n     * @param finalTop  Target top position for the captured view\n     * @param xvel      Horizontal velocity\n     * @param yvel      Vertical velocity\n     * @return true if animation should continue through {@link #continueSettling(boolean)} calls\n     */\n    private boolean settleContentViewAt(int finalLeft, int finalTop, int xvel, int yvel) {\n        final int startLeft = mContentView.getLeft();\n        final int startTop = mContentView.getTop();\n        final int dx = finalLeft - startLeft;\n        final int dy = finalTop - startTop;\n\n        if (dx == 0 && dy == 0) {\n            // Nothing to do. Send callbacks, be done.\n            mScroller.abortAnimation();\n            setDragState(STATE_IDLE);\n            return false;\n        }\n\n        final int duration = computeSettleDuration(dx, dy, xvel, yvel);\n        mScroller.startScroll(startLeft, startTop, dx, dy, duration);\n\n        setDragState(STATE_SETTLING);\n        invalidate();\n        return true;\n    }\n\n    public boolean continueSettling(boolean deferCallbacks) {\n        if (mDragState == STATE_SETTLING) {\n            boolean keepGoing = mScroller.computeScrollOffset();\n            final int x = mScroller.getCurrX();\n            final int y = mScroller.getCurrY();\n            mViewOffsetHelper.setOffset(\n                    x - mViewOffsetHelper.getLayoutLeft(),\n                    y - mViewOffsetHelper.getLayoutTop());\n            onScroll();\n\n            if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {\n                // Close enough. The interpolator/scroller might think we're still moving\n                // but the user sure doesn't.\n                mScroller.abortAnimation();\n                keepGoing = false;\n            }\n\n            if (!keepGoing) {\n                if (deferCallbacks) {\n                    post(mSetIdleRunnable);\n                } else {\n                    setDragState(STATE_IDLE);\n                }\n            }\n        }\n\n        return mDragState == STATE_SETTLING;\n    }\n\n    private int computeSettleDuration(int dx, int dy, int xvel, int yvel) {\n        xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity);\n        yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity);\n        final int absDx = Math.abs(dx);\n        final int absDy = Math.abs(dy);\n        final int absXVel = Math.abs(xvel);\n        final int absYVel = Math.abs(yvel);\n        final int addedVel = absXVel + absYVel;\n        final int addedDistance = absDx + absDy;\n\n        final float xweight = xvel != 0 ? (float) absXVel / addedVel :\n                (float) absDx / addedDistance;\n        final float yweight = yvel != 0 ? (float) absYVel / addedVel :\n                (float) absDy / addedDistance;\n\n        int range = mViewMoveAction.getDragRange(this, mCurrentDragDirection);\n        int xduration = computeAxisDuration(dx, xvel, range);\n        int yduration = computeAxisDuration(dy, yvel, range);\n\n        return (int) (xduration * xweight + yduration * yweight);\n    }\n\n    private int computeAxisDuration(int delta, int velocity, int motionRange) {\n        if (delta == 0) {\n            return 0;\n        }\n\n        final int width = getWidth();\n        final int halfWidth = width / 2;\n        final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width);\n        final float distance = halfWidth + halfWidth\n                * distanceInfluenceForSnapDuration(distanceRatio);\n\n        int duration;\n        velocity = Math.abs(velocity);\n        if (velocity > 0) {\n            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));\n        } else if (motionRange != 0) {\n            final float range = (float) Math.abs(delta) / motionRange;\n            duration = (int) ((range + 1) * BASE_SETTLE_DURATION);\n        } else {\n            duration = BASE_SETTLE_DURATION;\n        }\n        return Math.min(duration, MAX_SETTLE_DURATION);\n    }\n\n    private float distanceInfluenceForSnapDuration(float f) {\n        f -= 0.5f; // center the values about 0.\n        f *= 0.3f * (float) Math.PI / 2.0f;\n        return (float) Math.sin(f);\n    }\n\n    /**\n     * Clamp the magnitude of value for absMin and absMax.\n     * If the value is below the minimum, it will be clamped to zero.\n     * If the value is above the maximum, it will be clamped to the maximum.\n     *\n     * @param value  Value to clamp\n     * @param absMin Absolute value of the minimum significant value to return\n     * @param absMax Absolute value of the maximum value to return\n     * @return The clamped value with the same sign as <code>value</code>\n     */\n    private int clampMag(int value, int absMin, int absMax) {\n        final int absValue = Math.abs(value);\n        if (absValue < absMin) return 0;\n        if (absValue > absMax) return value > 0 ? absMax : -absMax;\n        return value;\n    }\n\n    /**\n     * Clamp the magnitude of value for absMin and absMax.\n     * If the value is below the minimum, it will be clamped to zero.\n     * If the value is above the maximum, it will be clamped to the maximum.\n     *\n     * @param value  Value to clamp\n     * @param absMin Absolute value of the minimum significant value to return\n     * @param absMax Absolute value of the maximum value to return\n     * @return The clamped value with the same sign as <code>value</code>\n     */\n    private float clampMag(float value, float absMin, float absMax) {\n        final float absValue = Math.abs(value);\n        if (absValue < absMin) return 0;\n        if (absValue > absMax) return value > 0 ? absMax : -absMax;\n        return value;\n    }\n\n    public void cancel() {\n        if (mVelocityTracker != null) {\n            mVelocityTracker.recycle();\n            mVelocityTracker = null;\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (mViewOffsetHelper != null) {\n            mViewOffsetHelper.onViewLayout();\n        }\n    }\n\n    @Override\n    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {\n        final boolean drawContent = child == mContentView;\n\n        boolean ret = super.drawChild(canvas, child, drawingTime);\n        if (mScrimOpacity > 0 && drawContent\n                && mDragState != STATE_IDLE) {\n            drawShadow(canvas, child);\n            drawScrim(canvas, child);\n        }\n        return ret;\n    }\n\n\n    private void drawScrim(Canvas canvas, View child) {\n        final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;\n        final int alpha = (int) (baseAlpha * mScrimOpacity);\n        final int color = alpha << 24 | (mScrimColor & 0xffffff);\n        int movingEdge = mViewMoveAction.getEdge(mCurrentDragDirection);\n        if ((movingEdge & EDGE_LEFT) != 0) {\n            canvas.clipRect(0, 0, child.getLeft(), getHeight());\n        } else if ((movingEdge & EDGE_RIGHT) != 0) {\n            canvas.clipRect(child.getRight(), 0, getRight(), getHeight());\n        } else if ((movingEdge & EDGE_BOTTOM) != 0) {\n            canvas.clipRect(0, child.getBottom(), getRight(), getHeight());\n        } else if ((movingEdge & EDGE_TOP) != 0) {\n            canvas.clipRect(0, 0, getRight(), child.getTop());\n        }\n        canvas.drawColor(color);\n    }\n\n    private void drawShadow(Canvas canvas, View child) {\n\n\n        int movingEdge = mViewMoveAction.getEdge(mCurrentDragDirection);\n        if ((movingEdge & EDGE_LEFT) != 0) {\n            mShadowLeft.setBounds(child.getLeft() - mShadowLeft.getIntrinsicWidth(),\n                    child.getTop(), child.getLeft(), child.getBottom());\n            mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA));\n            mShadowLeft.draw(canvas);\n        } else if ((movingEdge & EDGE_RIGHT) != 0) {\n            mShadowRight.setBounds(child.getRight(), child.getTop(),\n                    child.getRight() + mShadowRight.getIntrinsicWidth(), child.getBottom());\n            mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA));\n            mShadowRight.draw(canvas);\n        } else if ((movingEdge & EDGE_BOTTOM) != 0) {\n            mShadowBottom.setBounds(child.getLeft(), child.getBottom(), child.getRight(),\n                    child.getBottom() + mShadowBottom.getIntrinsicHeight());\n            mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA));\n            mShadowBottom.draw(canvas);\n        } else if ((movingEdge & EDGE_TOP) != 0) {\n            mShadowTop.setBounds(child.getLeft(), child.getTop() - mShadowTop.getIntrinsicHeight(),\n                    child.getRight(), child.getTop());\n            mShadowTop.setAlpha((int) (mScrimOpacity * FULL_ALPHA));\n            mShadowTop.draw(canvas);\n        }\n    }\n\n    @Override\n    public void computeScroll() {\n        if (continueSettling(true)) {\n            ViewCompat.postInvalidateOnAnimation(this);\n        }\n    }\n\n    private void onSwipeBackBegin() {\n        mIsScrollOverValid = true;\n        mScrimOpacity = 1 - mViewMoveAction.getCurrentPercent(this, mContentView, mCurrentDragDirection);\n        if (mListeners != null && !mListeners.isEmpty()) {\n            for (SwipeListener listener : mListeners) {\n                listener.onSwipeBackBegin(mCurrentDragDirection, mViewMoveAction.getEdge(mCurrentDragDirection));\n            }\n        }\n        invalidate();\n    }\n\n    private void onScroll() {\n        float scrollPercent = mViewMoveAction.getCurrentPercent(this, mContentView, mCurrentDragDirection);\n        mScrimOpacity = 1 - mViewMoveAction.getCurrentPercent(this, mContentView, mCurrentDragDirection);\n        if (scrollPercent < mScrollThreshold && !mIsScrollOverValid) {\n            mIsScrollOverValid = true;\n        }\n        if (mDragState == STATE_DRAGGING && mIsScrollOverValid &&\n                scrollPercent >= mScrollThreshold) {\n            mIsScrollOverValid = false;\n            onScrollOverThreshold();\n        }\n        if (mListeners != null && !mListeners.isEmpty()) {\n            for (SwipeListener listener : mListeners) {\n                listener.onScroll(mCurrentDragDirection, mViewMoveAction.getEdge(mCurrentDragDirection), scrollPercent);\n            }\n        }\n        invalidate();\n    }\n\n    private void onScrollOverThreshold() {\n        if (mListeners != null && !mListeners.isEmpty()) {\n            for (SwipeListener listener : mListeners) {\n                listener.onScrollOverThreshold();\n            }\n        }\n    }\n\n    private void onViewDragStateChanged(int dragState) {\n        if (mListeners != null && !mListeners.isEmpty()) {\n            for (SwipeListener listener : mListeners) {\n                listener.onScrollStateChange(dragState,\n                        mViewMoveAction.getCurrentPercent(this, mContentView, mCurrentDragDirection));\n            }\n        }\n    }\n\n    public void resetOffset(){\n        if(mViewOffsetHelper != null){\n            mViewOffsetHelper.setOffset(0, 0);\n        }\n    }\n\n    public static SwipeBackLayout wrap(View child, ViewMoveAction viewMoveAction, Callback callback) {\n        SwipeBackLayout wrapper = new SwipeBackLayout(child.getContext());\n        wrapper.addView(child, new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        wrapper.setContentView(child);\n        wrapper.setViewMoveAction(viewMoveAction);\n        wrapper.setCallback(callback);\n        return wrapper;\n    }\n\n    public static SwipeBackLayout wrap(Context context, int childRes, ViewMoveAction viewMoveAction, Callback callback) {\n        SwipeBackLayout wrapper = new SwipeBackLayout(context);\n        View child = LayoutInflater.from(context).inflate(childRes, wrapper, false);\n        wrapper.addView(child, new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        wrapper.setContentView(child);\n        wrapper.setCallback(callback);\n        wrapper.setViewMoveAction(viewMoveAction);\n        return wrapper;\n    }\n\n    public static void translateInSwipeBack(View view, int edgeFlag, int targetOffset){\n        if (edgeFlag == EDGE_BOTTOM) {\n            view.setTranslationY(targetOffset);\n            view.setTranslationX(0);\n        } else if (edgeFlag == EDGE_RIGHT) {\n            view.setTranslationY(0);\n            view.setTranslationX(targetOffset);\n        } else if(edgeFlag == EDGE_LEFT){\n            view.setTranslationY(0);\n            view.setTranslationX(-targetOffset);\n        }else{\n            view.setTranslationY(-targetOffset);\n            view.setTranslationX(0);\n        }\n    }\n\n    public float getXFraction() {\n        int width = getWidth();\n        if(width == 0){\n            ViewParent parent = getParent();\n            if(parent instanceof ViewGroup){\n                width = ((ViewGroup)parent).getWidth();\n            }\n        }\n        return (width == 0) ? 0 : getX() / (float) width;\n    }\n\n    public void setXFraction(float xFraction) {\n        int width = getWidth();\n        if(width == 0){\n            ViewParent parent = getParent();\n            if(parent instanceof ViewGroup){\n                width = ((ViewGroup)parent).getWidth();\n            }\n        }\n        setX((width > 0) ? (xFraction * width) : 0);\n    }\n\n    public float getYFraction() {\n        int height = getHeight();\n        if(height == 0){\n            ViewParent parent = getParent();\n            if(parent instanceof ViewGroup){\n                height = ((ViewGroup)parent).getHeight();\n            }\n        }\n        return (height == 0) ? 0 : getY() / (float) height;\n    }\n\n    public void setYFraction(float yFraction) {\n        int height = getHeight();\n        if(height == 0){\n            ViewParent parent = getParent();\n            if(parent instanceof ViewGroup){\n                height = ((ViewGroup)parent).getHeight();\n            }\n        }\n        setY((height > 0) ? (yFraction * height) : 0);\n    }\n\n    public interface Callback {\n        int getDragDirection(SwipeBackLayout swipeBackLayout, ViewMoveAction moveAction,\n                             float downX, float downY, float dx, float dy, float touchSlop);\n\n        void reportFrequentlyRequestLayout(int count, long duration);\n    }\n\n    public interface ViewMoveAction {\n        float getCurrentPercent(@NonNull SwipeBackLayout swipeBackLayout,\n                                @NonNull View contentView, int dragDirection);\n\n        int getDragRange(@NonNull SwipeBackLayout swipeBackLayout, int dragDirection);\n\n        int getSettleTarget(@NonNull SwipeBackLayout swipeBackLayout,\n                            @NonNull View contentView,\n                            float v, int dragDirection, float scrollThreshold);\n\n        int getEdge(int dragDirection);\n\n        void move(@NonNull SwipeBackLayout swipeBackLayout,\n                  @NonNull View contentView,\n                  @NonNull QMUIViewOffsetHelper offsetHelper,\n                  int dragDirection, float delta);\n    }\n\n    public interface ListenerRemover {\n        void remove();\n    }\n\n    public interface SwipeListener {\n        /**\n         * Invoke when state change\n         *\n         * @param state         flag to describe scroll state\n         * @param scrollPercent scroll percent of this view\n         * @see #STATE_IDLE\n         * @see #STATE_DRAGGING\n         * @see #STATE_SETTLING\n         */\n        void onScrollStateChange(int state, float scrollPercent);\n\n        /**\n         * Invoke when scrolling\n         *\n         * @param moveEdge      flag to describe edge\n         * @param scrollPercent scroll percent of this view\n         */\n        void onScroll(int dragDirection, int moveEdge, float scrollPercent);\n\n        /**\n         * Invoke when swipe back begin.\n         */\n        void onSwipeBackBegin(int dragDirection, int moveEdge);\n\n        /**\n         * Invoke when scroll percent over the threshold for the first time\n         */\n        void onScrollOverThreshold();\n    }\n\n    public interface OnInsetsHandler {\n        @WindowInsetsCompat.Type.InsetsType\n        int getInsetsType();\n    }\n\n    public static class ViewMoveAuto implements ViewMoveAction {\n\n        private boolean isHor(int dragDirection){\n            return dragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT ||\n                    dragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT;\n        }\n\n        @Override\n        public float getCurrentPercent(@NonNull SwipeBackLayout swipeBackLayout,\n                                       @NonNull View contentView, int dragDirection) {\n            float percent;\n            if(isHor(dragDirection)){\n                percent = Math.abs(contentView.getLeft() * 1f / swipeBackLayout.getWidth());\n            }else{\n                percent = Math.abs(contentView.getTop() * 1f / swipeBackLayout.getHeight());\n            }\n            return QMUILangHelper.constrain(percent, 0f, 1f);\n        }\n\n        @Override\n        public int getDragRange(@NonNull SwipeBackLayout swipeBackLayout, int dragDirection) {\n            if(isHor(dragDirection)){\n                return swipeBackLayout.getWidth();\n            }\n            return swipeBackLayout.getHeight();\n        }\n\n        @Override\n        public int getSettleTarget(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull View contentView,\n                                   float v, int dragDirection, float scrollThreshold) {\n            if(dragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT){\n                if (v > 0 ||\n                        (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                    return swipeBackLayout.getWidth();\n                }\n            }else if(dragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT){\n                if (v < 0 ||\n                        (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                    return -swipeBackLayout.getWidth();\n                }\n            }else if(dragDirection == DRAG_DIRECTION_TOP_TO_BOTTOM){\n                if (v > 0 ||\n                        (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                    return swipeBackLayout.getHeight();\n                }\n            }else{\n                if (v < 0 ||\n                        (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                    return -swipeBackLayout.getHeight();\n                }\n            }\n\n            return 0;\n        }\n\n        @Override\n        public int getEdge(int dragDirection) {\n            if(dragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT){\n                return EDGE_LEFT;\n            }else if(dragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT){\n                return EDGE_RIGHT;\n            }else if(dragDirection == DRAG_DIRECTION_TOP_TO_BOTTOM){\n                return EDGE_TOP;\n            }else{\n                return EDGE_BOTTOM;\n            }\n        }\n\n        @Override\n        public void move(@NonNull SwipeBackLayout swipeBackLayout,\n                         @NonNull View contentView,\n                         @NonNull QMUIViewOffsetHelper offsetHelper,\n                         int dragDirection, float delta) {\n            if(dragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT){\n                int target = (int) (offsetHelper.getLeftAndRightOffset() + delta);\n                target = QMUILangHelper.constrain(target, 0, swipeBackLayout.getWidth());\n                offsetHelper.setLeftAndRightOffset(target);\n            }else if(dragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT){\n                int target = (int) (offsetHelper.getLeftAndRightOffset() + delta);\n                target = QMUILangHelper.constrain(target, -swipeBackLayout.getWidth(),0);\n                offsetHelper.setLeftAndRightOffset(target);\n            }else if(dragDirection == DRAG_DIRECTION_TOP_TO_BOTTOM){\n                int target = (int) (offsetHelper.getTopAndBottomOffset() + delta);\n                target = QMUILangHelper.constrain(target, 0, swipeBackLayout.getHeight());\n                offsetHelper.setTopAndBottomOffset(target);\n            }else{\n                int target = (int) (offsetHelper.getTopAndBottomOffset() + delta);\n                target = QMUILangHelper.constrain(target, -swipeBackLayout.getHeight(),0);\n                offsetHelper.setTopAndBottomOffset(target);\n            }\n\n        }\n    }\n\n    public static class ViewMoveLeftToRight implements ViewMoveAction {\n\n        @Override\n        public float getCurrentPercent(@NonNull SwipeBackLayout swipeBackLayout,\n                                       @NonNull View contentView, int dragDirection) {\n            return QMUILangHelper.constrain(\n                    contentView.getLeft() * 1f / swipeBackLayout.getWidth(), 0f, 1f);\n        }\n\n        @Override\n        public int getDragRange(@NonNull SwipeBackLayout swipeBackLayout, int dragDirection) {\n            return swipeBackLayout.getWidth();\n        }\n\n        @Override\n        public int getSettleTarget(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull View contentView,\n                                   float v, int dragDirection, float scrollThreshold) {\n            if (v > 0 ||\n                    (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                return swipeBackLayout.getWidth();\n            }\n            return 0;\n        }\n\n        @Override\n        public int getEdge(int dragDirection) {\n            return EDGE_LEFT;\n        }\n\n        @Override\n        public void move(@NonNull SwipeBackLayout swipeBackLayout,\n                         @NonNull View contentView,\n                         @NonNull QMUIViewOffsetHelper offsetHelper, int dragDirection, float delta) {\n            if (dragDirection == DRAG_DIRECTION_BOTTOM_TO_TOP ||\n                    dragDirection == DRAG_DIRECTION_TOP_TO_BOTTOM) {\n                delta = delta * swipeBackLayout.getWidth() / swipeBackLayout.getHeight();\n            }\n            int target = (int) (offsetHelper.getLeftAndRightOffset() + delta);\n            target = QMUILangHelper.constrain(target, 0, swipeBackLayout.getWidth());\n            offsetHelper.setLeftAndRightOffset(target);\n        }\n    }\n\n    public static class ViewMoveTopToBottom implements ViewMoveAction {\n\n        @Override\n        public float getCurrentPercent(@NonNull SwipeBackLayout swipeBackLayout,\n                                       @NonNull View contentView, int dragDirection) {\n            return QMUILangHelper.constrain(\n                    contentView.getTop() * 1f / swipeBackLayout.getHeight(), 0f, 1f);\n        }\n\n        @Override\n        public int getDragRange(@NonNull SwipeBackLayout swipeBackLayout, int dragDirection) {\n            return swipeBackLayout.getHeight();\n        }\n\n        @Override\n        public int getSettleTarget(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull View contentView,\n                                   float v, int dragDirection, float scrollThreshold) {\n            if (v > 0 ||\n                    (v == 0 && getCurrentPercent(swipeBackLayout, contentView, dragDirection) > scrollThreshold)) {\n                return swipeBackLayout.getHeight();\n            }\n            return 0;\n        }\n\n        @Override\n        public int getEdge(int dragDirection) {\n            return EDGE_TOP;\n        }\n\n        @Override\n        public void move(@NonNull SwipeBackLayout swipeBackLayout,\n                         @NonNull View contentView,\n                         @NonNull QMUIViewOffsetHelper offsetHelper, int dragDirection, float delta) {\n            if (dragDirection == DRAG_DIRECTION_LEFT_TO_RIGHT ||\n                    dragDirection == DRAG_DIRECTION_RIGHT_TO_LEFT) {\n                delta = delta * swipeBackLayout.getHeight() / swipeBackLayout.getWidth();\n            }\n            int target = (int) (offsetHelper.getTopAndBottomOffset() + delta);\n            target = QMUILangHelper.constrain(target, 0, swipeBackLayout.getHeight());\n            offsetHelper.setTopAndBottomOffset(target);\n        }\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/SwipeBackgroundView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.os.IBinder;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport com.qmuiteam.qmui.util.QMUIColorHelper;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SwipeBackgroundView extends View {\n\n    private ArrayList<ViewInfo> mViewWeakReference;\n    private boolean mDoRotate = false;\n\n    public SwipeBackgroundView(Context context, boolean forceDisableHardwareAccelerated) {\n        super(context);\n        if(forceDisableHardwareAccelerated){\n            setLayerType(LAYER_TYPE_SOFTWARE, null);\n        }\n    }\n\n    public void bind(Activity activity, Activity swipeActivity, boolean restoreForSubWindow) {\n        mDoRotate = false;\n        if (mViewWeakReference != null) {\n            mViewWeakReference.clear();\n        }\n        int orientation = activity.getResources().getConfiguration().orientation;\n        if (orientation != getResources().getConfiguration().orientation) {\n            // the screen orientation changed, reMeasure and reLayout\n            int requestedOrientation = activity.getRequestedOrientation();\n            if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||\n                    requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {\n                // TODO is it suitable for fixed screen orientation\n                // the prev activity has locked the screen orientation\n                mDoRotate = true;\n            } else if (swipeActivity instanceof InnerBaseActivity) {\n                swipeActivity.getWindow().getDecorView().setBackgroundColor(0);\n                ((InnerBaseActivity) swipeActivity).convertToTranslucentCauseOrientationChanged();\n                invalidate();\n                return;\n            }\n        }\n\n        if (!restoreForSubWindow) {\n            View contentView = activity.findViewById(Window.ID_ANDROID_CONTENT);\n            if (mViewWeakReference == null) {\n                mViewWeakReference = new ArrayList<>();\n            }\n            mViewWeakReference.add(new ViewInfo(contentView, null, true));\n            invalidate();\n            return;\n        }\n\n        try {\n            IBinder windowToken = activity.getWindow().getDecorView().getWindowToken();\n            Field windowManagerGlobalField = activity.getWindowManager().getClass().getDeclaredField(\"mGlobal\");\n            windowManagerGlobalField.setAccessible(true);\n            Object windowManagerGlobal = windowManagerGlobalField.get(activity.getWindowManager());\n            if (windowManagerGlobal != null) {\n                Field viewsField = windowManagerGlobal.getClass().getDeclaredField(\"mViews\");\n                viewsField.setAccessible(true);\n                Field paramsField = windowManagerGlobal.getClass().getDeclaredField(\"mParams\");\n                paramsField.setAccessible(true);\n                List<WindowManager.LayoutParams> params = (List<WindowManager.LayoutParams>) paramsField.get(windowManagerGlobal);\n                List<View> views = (List<View>) viewsField.get(windowManagerGlobal);\n                IBinder activityToken = null;\n                // reverse order\n                for (int i = params.size() - 1; i >= 0; i--) {\n                    WindowManager.LayoutParams lp = params.get(i);\n                    View view = views.get(i);\n                    if (view.getWindowToken() == windowToken) {\n                        activityToken = lp.token;\n                        break;\n                    }\n                }\n                if (activityToken != null) {\n                    // reverse order\n                    for (int i = params.size() - 1; i >= 0; i--) {\n                        WindowManager.LayoutParams lp = params.get(i);\n                        View view = views.get(i);\n                        boolean isMain = view.getWindowToken() == windowToken;\n                        // Dialog use activityToken in lp\n                        // PopupWindow use windowToken in lp\n                        if (isMain || lp.token == activityToken || lp.token == windowToken) {\n                            View prevContentView = view.findViewById(Window.ID_ANDROID_CONTENT);\n                            if (mViewWeakReference == null) {\n                                mViewWeakReference = new ArrayList<>();\n                            }\n                            if (prevContentView != null) {\n                                mViewWeakReference.add(new ViewInfo(prevContentView, lp, isMain));\n                            }else {\n                                // PopupWindow doest not exist a descendant view with id Window.ID_ANDROID_CONTENT\n                                mViewWeakReference.add(new ViewInfo(view, lp, isMain));\n                            }\n                        }\n                    }\n                }\n\n            }\n        } catch (Exception ignored) {\n\n        } finally {\n            // sure get one view\n            if (mViewWeakReference == null || mViewWeakReference.isEmpty()) {\n                View contentView = activity.findViewById(Window.ID_ANDROID_CONTENT);\n                if (mViewWeakReference == null) {\n                    mViewWeakReference = new ArrayList<>();\n                }\n                mViewWeakReference.add(new ViewInfo(contentView, null, true));\n            }\n        }\n        invalidate();\n    }\n\n    public void unBind() {\n        if (mViewWeakReference != null) {\n            mViewWeakReference.clear();\n        }\n        mViewWeakReference = null;\n        mDoRotate = false;\n    }\n\n    boolean hasChildWindow() {\n        return mViewWeakReference != null && mViewWeakReference.size() > 1;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        if (mViewWeakReference != null && mViewWeakReference.size() > 0) {\n            if (mDoRotate) {\n                canvas.translate(0, getHeight());\n                canvas.rotate(-90, 0, 0);\n            }\n            // reverse order\n            for (int i = mViewWeakReference.size() - 1; i >= 0; i--) {\n                mViewWeakReference.get(i).draw(canvas);\n            }\n\n        }\n    }\n\n    static class ViewInfo {\n        WeakReference<View> viewRef;\n        WindowManager.LayoutParams lp;\n        boolean isMain;\n        private int[] tempLocations = new int[2];\n\n        public ViewInfo(@NonNull View view, @Nullable WindowManager.LayoutParams lp, boolean isMain) {\n            this.viewRef = new WeakReference<>(view);\n            this.lp = lp;\n            this.isMain = isMain;\n        }\n\n        void draw(Canvas canvas) {\n            View view = viewRef.get();\n            if (view != null) {\n                if (isMain || lp == null) {\n                    view.draw(canvas);\n                } else {\n                    if((lp.flags & WindowManager.LayoutParams.FLAG_DIM_BEHIND) != 0){\n                        canvas.drawColor(QMUIColorHelper.setColorAlpha(Color.BLACK, lp.dimAmount));\n                    }\n                    view.getLocationOnScreen(tempLocations);\n                    canvas.translate(tempLocations[0], tempLocations[1]);\n                    view.draw(canvas);\n                    canvas.translate(-tempLocations[0], -tempLocations[1]);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/Utils.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.ActivityOptions;\nimport android.os.Looper;\n\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\n\nimport com.qmuiteam.qmui.QMUILog;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\n/**\n * Created by Chaojun Wang on 6/9/14.\n */\npublic class Utils {\n    private Utils() {\n    }\n\n    /**\n     * Convert a translucent themed Activity\n     * {@link android.R.attr#windowIsTranslucent} to a fullscreen opaque\n     * Activity.\n     * <p>\n     * Call this whenever the background of a translucent Activity has changed\n     * to become opaque. Doing so will allow the {@link android.view.Surface} of\n     * the Activity behind to be released.\n     * <p>\n     * This call has no effect on non-translucent activities or on activities\n     * with the {@link android.R.attr#windowIsFloating} attribute.\n     */\n    public static void convertActivityFromTranslucent(Activity activity) {\n        try {\n            @SuppressLint(\"PrivateApi\") Method method = Activity.class.getDeclaredMethod(\"convertFromTranslucent\");\n            method.setAccessible(true);\n            method.invoke(activity);\n        } catch (Throwable ignore) {\n        }\n    }\n\n    /**\n     * Convert a translucent themed Activity\n     * {@link android.R.attr#windowIsTranslucent} back from opaque to\n     * translucent following a call to\n     * {@link #convertActivityFromTranslucent(android.app.Activity)} .\n     * <p>\n     * Calling this allows the Activity behind this one to be seen again. Once\n     * all such Activities have been redrawn\n     * <p>\n     * This call has no effect on non-translucent activities or on activities\n     * with the {@link android.R.attr#windowIsFloating} attribute.\n     */\n    public static void convertActivityToTranslucent(Activity activity) {\n        try {\n            @SuppressLint({\"PrivateApi\", \"DiscouragedPrivateApi\"}) Method getActivityOptions = Activity.class.getDeclaredMethod(\"getActivityOptions\");\n            getActivityOptions.setAccessible(true);\n            Object options = getActivityOptions.invoke(activity);\n\n            Class<?>[] classes = Activity.class.getDeclaredClasses();\n            Class<?> translucentConversionListenerClazz = null;\n            for (Class clazz : classes) {\n                if (clazz.getSimpleName().contains(\"TranslucentConversionListener\")) {\n                    translucentConversionListenerClazz = clazz;\n                }\n            }\n            @SuppressLint(\"PrivateApi\") Method convertToTranslucent = Activity.class.getDeclaredMethod(\"convertToTranslucent\",\n                    translucentConversionListenerClazz, ActivityOptions.class);\n            convertToTranslucent.setAccessible(true);\n            convertToTranslucent.invoke(activity, null, options);\n        } catch (Throwable ignore) {\n        }\n    }\n\n\n    public static void assertInMainThread() {\n        if (Looper.myLooper() != Looper.getMainLooper()) {\n            StackTraceElement[] elements = Thread.currentThread().getStackTrace();\n            String methodMsg = null;\n            if (elements != null && elements.length >= 4) {\n                methodMsg = elements[3].toString();\n            }\n            throw new IllegalStateException(\"Call the method must be in main thread: \" + methodMsg);\n        }\n    }\n\n    static void modifyOpForStartFragmentAndDestroyCurrent(FragmentManager fragmentManager,\n                                                                 final QMUIFragment fragment,\n                                                                 final boolean useNewTransitionConfigWhenPop,\n                                                                 final QMUIFragment.TransitionConfig transitionConfig){\n        findAndModifyOpInBackStackRecord(fragmentManager, -1, new Utils.OpHandler() {\n            @Override\n            public boolean handle(Object op) {\n                Field cmdField = null;\n                try {\n                    cmdField = Utils.getOpCmdField(op);\n                    cmdField.setAccessible(true);\n                    int cmd = (int) cmdField.get(op);\n                    if (cmd == 1) {\n                        if (useNewTransitionConfigWhenPop) {\n                            Field popEnterAnimField = Utils.getOpPopEnterAnimField(op);\n                            popEnterAnimField.setAccessible(true);\n                            popEnterAnimField.set(op, transitionConfig.popenter);\n\n                            Field popExitAnimField = Utils.getOpPopExitAnimField(op);\n                            popExitAnimField.setAccessible(true);\n                            popExitAnimField.set(op, transitionConfig.popout);\n                        }\n\n                        Field oldFragmentField = Utils.getOpFragmentField(op);\n                        oldFragmentField.setAccessible(true);\n                        Object fragmentObj = oldFragmentField.get(op);\n                        oldFragmentField.set(op, fragment);\n                        Field backStackNestField = Fragment.class.getDeclaredField(\"mBackStackNesting\");\n                        backStackNestField.setAccessible(true);\n                        int oldFragmentBackStackNest = (int) backStackNestField.get(fragmentObj);\n                        backStackNestField.set(fragment, oldFragmentBackStackNest);\n                        backStackNestField.set(fragmentObj, --oldFragmentBackStackNest);\n                        return true;\n                    }\n                } catch (Throwable e) {\n                    e.printStackTrace();\n                }\n                return false;\n            }\n\n            @Override\n            public boolean needReNameTag() {\n                return true;\n            }\n\n            @Override\n            public String newTagName() {\n                return fragment.getClass().getSimpleName();\n            }\n        });\n    }\n\n    static void findAndModifyOpInBackStackRecord(FragmentManager fragmentManager, int backStackIndex, OpHandler handler) {\n        if (fragmentManager == null || handler == null) {\n            return;\n        }\n        int backStackCount = fragmentManager.getBackStackEntryCount();\n        if (backStackCount > 0) {\n            if (backStackIndex >= backStackCount || backStackIndex < -backStackCount) {\n                QMUILog.d(\"findAndModifyOpInBackStackRecord\", \"backStackIndex error: \" +\n                        \"backStackIndex = \" + backStackIndex + \" ; backStackCount = \" + backStackCount);\n                return;\n            }\n            if (backStackIndex < 0) {\n                backStackIndex = backStackCount + backStackIndex;\n            }\n            try {\n                FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(backStackIndex);\n\n                if (handler.needReNameTag()) {\n                    Field nameField = Utils.getNameField(backStackEntry);\n                    if (nameField != null) {\n                        nameField.setAccessible(true);\n                        nameField.set(backStackEntry, handler.newTagName());\n                    }\n                }\n\n\n                Field opsField = Utils.getOpsField(backStackEntry);\n                if(opsField != null){\n                    opsField.setAccessible(true);\n                    Object opsObj = opsField.get(backStackEntry);\n                    if (opsObj instanceof List<?>) {\n                        List<?> ops = (List<?>) opsObj;\n                        for (Object op : ops) {\n                            if (handler.handle(op)) {\n                                return;\n                            }\n                        }\n                    }\n                }\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private static boolean sOldBackStackEntryImpl = false;\n\n    static Field getBackStackEntryField(FragmentManager.BackStackEntry backStackEntry, String name) {\n        Field opsField = null;\n        if (!sOldBackStackEntryImpl) {\n            try {\n                opsField = FragmentTransaction.class.getDeclaredField(name);\n            } catch (NoSuchFieldException ignore) {\n            }\n        }\n\n        if (opsField == null) {\n            sOldBackStackEntryImpl = true;\n            try {\n                opsField = backStackEntry.getClass().getDeclaredField(name);\n            } catch (NoSuchFieldException ignore) {\n            }\n        }\n        return opsField;\n    }\n\n    static Field getOpsField(FragmentManager.BackStackEntry backStackEntry) {\n        return getBackStackEntryField(backStackEntry, \"mOps\");\n    }\n\n    static Field getNameField(FragmentManager.BackStackEntry backStackEntry) {\n        return getBackStackEntryField(backStackEntry, \"mName\");\n    }\n\n    private static boolean sOldOpImpl = false;\n\n    private static Field getOpField(Object op, String fieldNameNew, String fieldNameOld) {\n        Field field = null;\n        if (!sOldOpImpl) {\n            try {\n                field = op.getClass().getDeclaredField(fieldNameNew);\n            } catch (NoSuchFieldException ignore) {\n\n            }\n        }\n\n        if (field == null) {\n            sOldOpImpl = true;\n            try {\n                field = op.getClass().getDeclaredField(fieldNameOld);\n            } catch (NoSuchFieldException ignore) {\n            }\n        }\n        return field;\n    }\n\n    static Field getOpCmdField(Object op) {\n        return getOpField(op, \"mCmd\", \"cmd\");\n    }\n\n    static Field getOpFragmentField(Object op) {\n        return getOpField(op, \"mFragment\", \"fragment\");\n    }\n\n    static Field getOpPopEnterAnimField(Object op) {\n        return getOpField(op, \"mPopEnterAnim\", \"popEnterAnim\");\n    }\n\n    static Field getOpPopExitAnimField(Object op) {\n        return getOpField(op, \"mPopExitAnim\", \"popExitAnim\");\n    }\n\n    interface OpHandler {\n        boolean handle(Object op);\n\n        boolean needReNameTag();\n\n        String newTagName();\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/annotation/DefaultFirstFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.annotation;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface DefaultFirstFragment {\n    Class<? extends QMUIFragment> value();\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/Effect.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.effect;\n\npublic abstract class Effect {\n\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/FragmentResultEffect.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.effect;\n\nimport android.content.Intent;\n\nimport androidx.annotation.Nullable;\n\npublic class FragmentResultEffect extends Effect {\n    private final int mRequestFragmentUUid;\n    private final int mResultCode;\n    private final int mRequestCode;\n    @Nullable\n    private final Intent mIntent;\n\n    public FragmentResultEffect(int requestFragmentUUid, int resultCode, int requestCode, @Nullable Intent intent) {\n        mRequestFragmentUUid = requestFragmentUUid;\n        mResultCode = resultCode;\n        mRequestCode = requestCode;\n        mIntent = intent;\n    }\n\n    public int getRequestCode() {\n        return mRequestCode;\n    }\n\n    public int getResultCode() {\n        return mResultCode;\n    }\n\n    public Intent getIntent() {\n        return mIntent;\n    }\n\n    public int getRequestFragmentUUid() {\n        return mRequestFragmentUUid;\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/MapEffect.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.effect;\n\nimport java.util.Map;\n\npublic class MapEffect extends Effect {\n    private final Map<String, Object> mMap;\n\n    public MapEffect(Map<String, Object> map) {\n        mMap = map;\n    }\n\n\n    public Object getValue(String key) {\n        if (mMap == null) {\n            return null;\n        }\n        return mMap.get(key);\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/QMUIFragmentEffectHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.effect;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.Lifecycle;\n\nimport java.util.List;\n\npublic abstract class QMUIFragmentEffectHandler<T extends Effect> {\n\n    public enum HandlePolicy {\n        /**\n         * handle the effect immediately without lifeCycle check\n         */\n        Immediately,\n        /**\n         * handle the effect immediately if the lifecycle is after started.\n         */\n        ImmediatelyIfStarted,\n\n        /**\n         * handle the effect util next start event.\n         */\n        NextStartEvent\n    }\n\n    /**\n     * provide the handle policy to determine when to handle the effects.\n     * @return handle policy\n     */\n    public HandlePolicy provideHandlePolicy() {\n        return HandlePolicy.ImmediatelyIfStarted;\n    }\n\n    /**\n     * determine whether we need handle the effect or not.\n     * @param effect the effect to check\n     * @return true if we need handle the effect\n     */\n    public abstract boolean shouldHandleEffect(@NonNull T effect);\n\n    /**\n     * the time to handle effect depends on {@link HandlePolicy}.\n     * @param effect\n     */\n    public abstract void handleEffect(@NonNull T effect);\n\n    /**\n     * if the handle policy is not {@link HandlePolicy#Immediately}, we may need handle more than one effects.\n     * @param effects\n     */\n    public void handleEffect(@NonNull List<T> effects) {\n        for (T effect : effects) {\n            handleEffect(effect);\n        }\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/QMUIFragmentEffectRegistration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.effect;\n\npublic interface QMUIFragmentEffectRegistration {\n    void unregister();\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/QMUIFragmentEffectRegistry.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmui.arch.effect;\n\nimport android.util.ArraySet;\n\nimport androidx.annotation.MainThread;\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.Lifecycle;\nimport androidx.lifecycle.LifecycleEventObserver;\nimport androidx.lifecycle.LifecycleOwner;\nimport androidx.lifecycle.ViewModel;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.arch.QMUIFragment;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n\npublic class QMUIFragmentEffectRegistry extends ViewModel {\n\n    class PendingRegister<T extends Effect> implements QMUIFragmentEffectRegistration{\n        final LifecycleOwner lifecycleOwner;\n        final QMUIFragmentEffectHandler<T> effectHandler;\n        private QMUIFragmentEffectRegistration registration;\n\n\n        public PendingRegister(LifecycleOwner lifecycleOwner, QMUIFragmentEffectHandler<T> effectHandler){\n            this.lifecycleOwner = lifecycleOwner;\n            this.effectHandler = effectHandler;\n        }\n\n        public void doRegister(){\n            if(lifecycleOwner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.DESTROYED)){\n                return;\n            }\n            registration = register(lifecycleOwner, effectHandler);\n        }\n\n        @Override\n        public void unregister() {\n            if(registration != null){\n                registration.unregister();\n            }\n        }\n    }\n\n    private static final String TAG = \"FragmentEffectRegistry\";\n\n    private final AtomicInteger mNextRc = new AtomicInteger(0);\n\n    private final transient Map<Integer, EffectHandlerWrapper<?>> mKeyToHandler = new HashMap<>();\n    private transient int mNotifyEffectRunning = 0;\n    private final transient Set<Integer> mPendingRemoveKeys = new HashSet<>();\n    private final transient List<PendingRegister<?>> mPendingRegister = new ArrayList<>();\n\n\n    /**\n     * Register a new handler with this registry.\n     *\n     * This is normally called by a higher level convenience methods like\n     * {@link QMUIFragment#registerEffect}.\n     *\n     * @param lifecycleOwner a {@link LifecycleOwner} that makes this call.\n     * @param effectHandler the handler to handle effect\n     *\n     * @return a FragmentEffectRegistration that can be used to unregister an FragmentEffectHandler.\n     */\n    public <T extends Effect> QMUIFragmentEffectRegistration register(\n            @NonNull final LifecycleOwner lifecycleOwner,\n            @NonNull final QMUIFragmentEffectHandler<T> effectHandler) {\n        if(mNotifyEffectRunning > 0){\n            PendingRegister<T> pendingRegister = new PendingRegister<>(lifecycleOwner, effectHandler);\n            mPendingRegister.add(pendingRegister);\n            return pendingRegister;\n        }\n\n        final int rc = mNextRc.getAndIncrement();\n        Lifecycle lifecycle = lifecycleOwner.getLifecycle();\n        mKeyToHandler.put(rc, new EffectHandlerWrapper<T>(effectHandler, lifecycle));\n        lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner1, event) -> {\n            if (Lifecycle.Event.ON_DESTROY.equals(event)) {\n                unregister(rc);\n            }\n        });\n\n        return () -> QMUIFragmentEffectRegistry.this.unregister(rc);\n    }\n\n    /**\n     * Unregister a handler previously registered with {@link #register}. This shouldn't be\n     * called directly, but instead through {@link QMUIFragmentEffectRegistration#unregister()}.\n     *\n     * @param key the unique key used when registering a callback.\n     */\n    @MainThread\n    final void unregister(int key) {\n        if(mNotifyEffectRunning > 0){\n            mPendingRemoveKeys.add(key);\n            return;\n        }\n        safeUnregister(key);\n    }\n\n    private void safeUnregister(int key){\n        EffectHandlerWrapper<?> effectHandlerWrapper = mKeyToHandler.remove(key);\n        if (effectHandlerWrapper != null) {\n            effectHandlerWrapper.cancel();\n        }\n    }\n\n    /**\n     * notify the effect to handlers registered with {@link #register}.\n     *\n     * This is normally called by a higher level convenience methods like\n     * {@link QMUIFragment#notifyEffect}\n     * @param effect\n     */\n    public <T extends Effect> void notifyEffect(T effect) {\n        mNotifyEffectRunning++;\n        for (Integer key : mKeyToHandler.keySet()) {\n            EffectHandlerWrapper<?> wrapper = mKeyToHandler.get(key);\n            if (wrapper != null && wrapper.shouldHandleEffect(effect)) {\n                wrapper.pushOrHandleEffect(effect);\n            }\n        }\n        mNotifyEffectRunning--;\n        if(mNotifyEffectRunning == 0){\n            if(!mPendingRemoveKeys.isEmpty()){\n                for(Integer key: mPendingRemoveKeys){\n                    safeUnregister(key);\n                }\n                mPendingRemoveKeys.clear();\n            }\n            if(!mPendingRegister.isEmpty()){\n                for(PendingRegister<?> register: mPendingRegister){\n                    register.doRegister();\n                }\n                mPendingRegister.clear();\n            }\n        }\n    }\n\n    private static class EffectHandlerWrapper<T extends Effect> implements LifecycleEventObserver {\n        final QMUIFragmentEffectHandler<T> mHandler;\n        final Lifecycle mLifecycle;\n        ArrayList<T> mEffects = null;\n        final Class<? extends Effect> mEffectType;\n\n        EffectHandlerWrapper(QMUIFragmentEffectHandler<T> handler, Lifecycle lifecycle) {\n            mHandler = handler;\n            mLifecycle = lifecycle;\n            lifecycle.addObserver(this);\n            mEffectType = getHandlerEffectType(handler);\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        private Class<? extends Effect> getHandlerEffectType(QMUIFragmentEffectHandler handler) {\n            Class<? extends Effect> effectClz = null;\n            try {\n                Class<?> handlerCls = handler.getClass();\n                while (handlerCls != null && handlerCls.getSuperclass() != QMUIFragmentEffectHandler.class) {\n                    handlerCls = handlerCls.getSuperclass();\n                }\n                if (handlerCls != null) {\n                    Type type = handlerCls.getGenericSuperclass();\n                    if (type instanceof ParameterizedType) {\n                        Type[] params = ((ParameterizedType) type).getActualTypeArguments();\n                        if (params.length > 0) {\n                            effectClz = (Class<? extends Effect>) params[0];\n                        }\n                    }\n                }\n            } catch (Throwable ignore) {\n\n            }\n\n            if (effectClz == null) {\n                if (QMUIConfig.DEBUG) {\n                    throw new RuntimeException(\"Error to get FragmentEffectHandler's generic parameter type\");\n                } else {\n                    QMUILog.d(TAG, \"Error to get FragmentEffectHandler's generic parameter type\");\n                }\n            }\n\n            return effectClz;\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        boolean shouldHandleEffect(Effect effect) {\n            return mEffectType != null && mEffectType.isAssignableFrom(effect.getClass()) && mHandler.shouldHandleEffect((T) effect);\n        }\n\n        @MainThread\n        @SuppressWarnings(\"unchecked\")\n        void pushOrHandleEffect(Effect effect) {\n            QMUIFragmentEffectHandler.HandlePolicy policy = mHandler.provideHandlePolicy();\n            if (policy == QMUIFragmentEffectHandler.HandlePolicy.Immediately ||\n                    (policy == QMUIFragmentEffectHandler.HandlePolicy.ImmediatelyIfStarted &&\n                            mLifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED))) {\n                mHandler.handleEffect((T) effect);\n                return;\n            }\n\n            if (mEffects == null) {\n                mEffects = new ArrayList<>();\n            }\n            mEffects.add((T) effect);\n        }\n\n        @Override\n        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {\n            if (event == Lifecycle.Event.ON_START) {\n                if (mEffects != null && !mEffects.isEmpty()) {\n                    List<T> effects = mEffects;\n                    mEffects = null;\n                    if (effects.size() == 1) {\n                        mHandler.handleEffect(effects.get(0));\n                    } else {\n                        mHandler.handleEffect(effects);\n                    }\n                }\n            } else if (event == Lifecycle.Event.ON_DESTROY) {\n                cancel();\n            }\n        }\n\n        void cancel() {\n            mLifecycle.removeObserver(this);\n            mEffects = null;\n        }\n    }\n\n    @Override\n    protected void onCleared() {\n        super.onCleared();\n        for (Integer key : mKeyToHandler.keySet()) {\n            EffectHandlerWrapper effectHandlerWrapper = mKeyToHandler.get(key);\n            if (effectHandlerWrapper != null) {\n                effectHandlerWrapper.cancel();\n            }\n        }\n        mKeyToHandler.clear();\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/QMUIFragmentMapEffectHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.effect;\n\npublic abstract class QMUIFragmentMapEffectHandler extends QMUIFragmentEffectHandler<MapEffect> {\n\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/effect/QMUIFragmentResultEffectHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.effect;\n\npublic abstract class QMUIFragmentResultEffectHandler extends QMUIFragmentEffectHandler<FragmentResultEffect> {\n\n    @Override\n    public HandlePolicy provideHandlePolicy() {\n        return HandlePolicy.NextStartEvent;\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/DefaultLatestVisitStorage.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class DefaultLatestVisitStorage implements QMUILatestVisitStorage {\n\n    private static final String SP_NAME = \"qmui_latest_visit\";\n    private static final String SP_FRAGMENT_RECORD_ID = \"id_qmui_f_r\";\n    private static final String SP_ACTIVITY_RECORD_ID = \"id_qmui_a_r\";\n    private static final String SP_ACTIVITY_ARG_PREFIX = \"a_a_\";\n    private static final String SP_FRAGMENT_ARG_PREFIX = \"a_f_\";\n    private static final char SP_INT_ARG_TAG = 'i';\n    private static final char SP_LONG_ARG_TAG = 'l';\n    private static final char SP_FLOAT_ARG_TAG = 'f';\n    private static final char SP_BOOLEAN_ARG_TAG = 'b';\n    private static final char SP_STRING_ARG_TAG = 's';\n    private SharedPreferences sp;\n\n    public DefaultLatestVisitStorage(Context context) {\n        sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);\n    }\n\n    @Override\n    public int getFragmentRecordId() {\n        return sp.getInt(SP_FRAGMENT_RECORD_ID, NOT_EXIST);\n    }\n\n    @Nullable\n    @Override\n    public Map<String, RecordArgumentEditor.Argument> getFragmentArguments() {\n        HashMap<String, RecordArgumentEditor.Argument> ret = new HashMap<>();\n        for (Map.Entry<String, ?> entity : sp.getAll().entrySet()) {\n            String key = entity.getKey();\n            Object value = entity.getValue();\n            String prefix = SP_FRAGMENT_ARG_PREFIX;\n            if (key.startsWith(prefix)) {\n                char tag = key.charAt(prefix.length());\n                String realKey = key.substring(prefix.length() + 1);\n                if (tag == SP_INT_ARG_TAG) {\n                    ret.put(realKey, new RecordArgumentEditor.Argument(value, Integer.TYPE));\n                } else if (tag == SP_BOOLEAN_ARG_TAG) {\n                    ret.put(realKey, new RecordArgumentEditor.Argument(value, Boolean.TYPE));\n                } else if (tag == SP_LONG_ARG_TAG) {\n                    ret.put(realKey, new RecordArgumentEditor.Argument(value, Long.TYPE));\n                } else if (tag == SP_FLOAT_ARG_TAG) {\n                    ret.put(realKey, new RecordArgumentEditor.Argument(value, Float.TYPE));\n                } else if (tag == SP_STRING_ARG_TAG) {\n                    ret.put(realKey, new RecordArgumentEditor.Argument(value, String.class));\n                }\n            }\n        }\n        return ret;\n    }\n\n    @Override\n    public int getActivityRecordId() {\n        return sp.getInt(SP_ACTIVITY_RECORD_ID, NOT_EXIST);\n    }\n\n    @Override\n    public void getAndWriteActivityArgumentsToIntent(@NonNull Intent intent) {\n        for (Map.Entry<String, ?> entity : sp.getAll().entrySet()) {\n            String key = entity.getKey();\n            Object value = entity.getValue();\n            String prefix = SP_ACTIVITY_ARG_PREFIX;\n            if (key.startsWith(prefix)) {\n                char tag = key.charAt(prefix.length());\n                String realKey = key.substring(prefix.length() + 1);\n                if (tag == SP_INT_ARG_TAG) {\n                    intent.putExtra(realKey, (Integer) value);\n                } else if (tag == SP_BOOLEAN_ARG_TAG) {\n                    intent.putExtra(realKey, (Boolean) value);\n                } else if (tag == SP_LONG_ARG_TAG) {\n                    intent.putExtra(realKey, (Long) value);\n                } else if (tag == SP_FLOAT_ARG_TAG) {\n                    intent.putExtra(realKey, (Float) value);\n                } else if (tag == SP_STRING_ARG_TAG) {\n                    intent.putExtra(realKey, (String) value);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void clearFragmentStorage() {\n        SharedPreferences.Editor editor = sp.edit();\n        editor.remove(SP_FRAGMENT_RECORD_ID);\n        clearArgument(editor, SP_FRAGMENT_ARG_PREFIX);\n        editor.apply();\n    }\n\n    @Override\n    public void clearActivityStorage() {\n        SharedPreferences.Editor editor = sp.edit();\n        editor.remove(SP_ACTIVITY_RECORD_ID);\n        clearArgument(editor, SP_ACTIVITY_ARG_PREFIX);\n        editor.apply();\n    }\n\n    @Override\n    public void saveFragmentRecordInfo(int id, Map<String, RecordArgumentEditor.Argument> arguments) {\n        SharedPreferences.Editor editor = sp.edit();\n        editor.putInt(SP_FRAGMENT_RECORD_ID, id);\n        putArguments(editor, SP_FRAGMENT_ARG_PREFIX, arguments);\n        editor.apply();\n    }\n\n    @Override\n    public void saveActivityRecordInfo(int id, @Nullable Map<String, RecordArgumentEditor.Argument> arguments) {\n        SharedPreferences.Editor editor = sp.edit();\n        editor.putInt(SP_ACTIVITY_RECORD_ID, id);\n        putArguments(editor, SP_ACTIVITY_ARG_PREFIX, arguments);\n        editor.apply();\n    }\n\n    @Override\n    public void clearAll() {\n        SharedPreferences.Editor editor = sp.edit();\n        editor.clear();\n        editor.apply();\n    }\n\n    private void clearArgument(SharedPreferences.Editor editor, String prefix) {\n        for (String key : sp.getAll().keySet()) {\n            if (key.startsWith(prefix)) {\n                editor.remove(key);\n            }\n        }\n    }\n\n    private void putArguments(SharedPreferences.Editor editor,\n                              String prefix, Map<String, RecordArgumentEditor.Argument> arguments) {\n        // clear first\n        clearArgument(editor, prefix);\n\n        if (arguments != null && arguments.size() > 0) {\n            for (String name : arguments.keySet()) {\n                RecordArgumentEditor.Argument argument = arguments.get(name);\n                if (argument != null) {\n                    Class<?> type = argument.getType();\n                    Object value = argument.getValue();\n                    if (type == Integer.TYPE || type == Integer.class) {\n                        editor.putInt(prefix + SP_INT_ARG_TAG + name, (Integer) value);\n                    } else if (type == Boolean.TYPE || type == Boolean.class) {\n                        editor.putBoolean(prefix + SP_BOOLEAN_ARG_TAG + name, (Boolean) value);\n                    } else if (type == Float.TYPE || type == Float.class) {\n                        editor.putFloat(prefix + SP_FLOAT_ARG_TAG + name, (Float) value);\n                    } else if (type == Long.TYPE || type == Long.class) {\n                        editor.putLong(prefix + SP_LONG_ARG_TAG + name, (Long) value);\n                    } else if (type == String.class) {\n                        editor.putString(prefix + SP_STRING_ARG_TAG + name, (String) value);\n                    } else {\n                        throw new RuntimeException(String.format(\n                                \"Not support the type: %s\", type.getSimpleName()));\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/LatestVisitArgumentCollector.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\nimport com.qmuiteam.qmui.arch.QMUILatestVisit;\n\npublic interface LatestVisitArgumentCollector {\n\n    /**\n     * Called by {@link QMUILatestVisit} to collect argument value\n     * Notice: This is called before onResume. So It can not used to save data\n     * produced after fragment resumed.\n     * @param editor RecordArgumentEditor\n     */\n    void onCollectLatestVisitArgument(RecordArgumentEditor editor);\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/QMUILatestVisitStorage.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\npublic interface QMUILatestVisitStorage {\n\n    int NOT_EXIST = -1;\n\n    // Fragment stuff\n    void saveFragmentRecordInfo(int id, @Nullable Map<String, RecordArgumentEditor.Argument> arguments);\n\n    int getFragmentRecordId();\n\n    @Nullable\n    Map<String, RecordArgumentEditor.Argument> getFragmentArguments();\n\n\n    void clearFragmentStorage();\n\n\n    // Activity Stuff\n    void saveActivityRecordInfo(int id, @Nullable Map<String, RecordArgumentEditor.Argument> arguments);\n\n    int getActivityRecordId();\n\n    void getAndWriteActivityArgumentsToIntent(@NonNull Intent intent);\n\n    void clearActivityStorage();\n\n    void clearAll();\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/RecordArgumentEditor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\nimport android.os.Bundle;\n\nimport java.util.Map;\n\nimport androidx.annotation.Nullable;\n\npublic interface RecordArgumentEditor {\n\n    RecordArgumentEditor putString(String key, @Nullable String value);\n\n    RecordArgumentEditor putInt(String key, int value);\n\n    RecordArgumentEditor putLong(String key, long value);\n\n    RecordArgumentEditor putFloat(String key, float value);\n\n    RecordArgumentEditor putBoolean(String key, boolean value);\n\n    RecordArgumentEditor put(String key, RecordArgumentEditor.Argument argument);\n\n    RecordArgumentEditor remove(String key);\n\n    RecordArgumentEditor clear();\n\n    Map<String, Argument> getAll();\n\n    class Argument {\n        private Object value;\n        private Class<?> type;\n\n        public Argument(Object value, Class<?> type) {\n            this.value = value;\n            this.type = type;\n        }\n\n        public Object getValue() {\n            return value;\n        }\n\n        public Class<?> getType() {\n            return type;\n        }\n\n        public void putToBundle(Bundle bundle, String key){\n            if(type == Integer.TYPE){\n                bundle.putInt(key, (Integer)value);\n            }else if(type == Boolean.TYPE){\n                bundle.putBoolean(key, (Boolean) value);\n            }else if(type == Long.TYPE){\n                bundle.putLong(key, (Long) value);\n            }else if(type == Float.TYPE){\n                bundle.putFloat(key, (Float) value);\n            }else if(type == String.class){\n                bundle.putString(key, (String) value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/RecordArgumentEditorImpl.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\nimport androidx.annotation.Nullable;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\npublic class RecordArgumentEditorImpl implements RecordArgumentEditor {\n\n    private HashMap<String, Argument> mMap = new HashMap<>();\n\n    @Override\n\n    public synchronized RecordArgumentEditor putString(String key, @Nullable String value) {\n        mMap.put(key, new Argument(value, String.class));\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor putInt(String key, int value) {\n        mMap.put(key, new Argument(value, Integer.TYPE));\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor putLong(String key, long value) {\n        mMap.put(key, new Argument(value, Long.TYPE));\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor putFloat(String key, float value) {\n        mMap.put(key, new Argument(value, Float.TYPE));\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor putBoolean(String key, boolean value) {\n        mMap.put(key, new Argument(value, Boolean.TYPE));\n        return this;\n    }\n\n    @Override\n    public RecordArgumentEditor put(String key, Argument argument) {\n        mMap.put(key, argument);\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor remove(String key) {\n        mMap.remove(key);\n        return this;\n    }\n\n    @Override\n    public synchronized RecordArgumentEditor clear() {\n        mMap.clear();\n        return this;\n    }\n\n    @Override\n    public Map<String, Argument> getAll() {\n        return new HashMap<>(mMap);\n    }\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/record/RecordIdClassMap.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.record;\n\npublic interface RecordIdClassMap {\n\n    Class<?> getRecordClassById(int id);\n\n    int getIdByRecordClass(Class<?> clazz);\n}\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/ActivitySchemeItem.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.util.ArrayMap\nimport com.qmuiteam.qmui.QMUILog\n\nprivate val factories by lazy { ArrayMap<Class<out QMUISchemeIntentFactory>, QMUISchemeIntentFactory>() }\n\ninternal class ActivitySchemeItem(\n    private val activityClass: Class<out Activity>,\n    useRefreshIfMatchedCurrent: Boolean,\n    private val intentFactoryCls: Class<out QMUISchemeIntentFactory>?,\n    required: ArrayMap<String, String?>?,\n    keysForInt: Array<String>?,\n    keysForBool: Array<String>?,\n    keysForLong: Array<String>?,\n    keysForFloat: Array<String>?,\n    keysForDouble: Array<String>?,\n    defaultParams: Array<String>?,\n    schemeMatcherCls: Class<out QMUISchemeMatcher>?,\n    schemeValueConverterCls: Class<out QMUISchemeValueConverter>?\n) : SchemeItem(\n    required, useRefreshIfMatchedCurrent, keysForInt, keysForBool,\n    keysForLong, keysForFloat, keysForDouble, defaultParams, schemeMatcherCls, schemeValueConverterCls\n) {\n    override fun handle(\n        handler: QMUISchemeHandler,\n        handleContext: SchemeHandleContext,\n        schemeInfo: SchemeInfo\n    ): Boolean {\n        var factoryCls = intentFactoryCls\n        if (factoryCls == null) {\n            factoryCls = handler.defaultIntentFactory\n        }\n        var factory = factories[factoryCls]\n        if (factory == null) {\n            try {\n                factory = factoryCls.newInstance()\n                factories[factoryCls] = factory\n            } catch (e: Exception) {\n                QMUILog.printErrStackTrace(\n                    QMUISchemeHandler.TAG, e, \"error to instance QMUISchemeIntentFactory: %d\",\n                    factoryCls.simpleName\n                )\n            }\n        }\n        if (factory != null) {\n            val params = convertFrom(schemeInfo.params)\n            if (factory.shouldBlockJump(handleContext.activity, activityClass, params)) {\n                return false\n            }\n            val intent = factory.factory(handleContext.activity, activityClass, params, schemeInfo.origin)\n            if (handleContext.canUseRefresh() &&\n                isUseRefreshIfMatchedCurrent &&\n                activityClass == handleContext.activity::class.java &&\n                handleContext.activity is ActivitySchemeRefreshable\n            ) {\n                (handleContext.activity as ActivitySchemeRefreshable).refreshFromScheme(intent)\n            } else {\n                if (intent == null) {\n                    return false\n                }\n                handleContext.pushActivity(activityClass, intent, factory)\n\n                if (shouldFinishCurrent(params)) {\n                    handleContext.shouldFinishCurrent = true\n                }\n            }\n            return true\n        }\n        return false\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/FragmentSchemeItem.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.util.ArrayMap\nimport com.qmuiteam.qmui.QMUILog\nimport com.qmuiteam.qmui.arch.QMUIFragment\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity\nimport com.qmuiteam.qmui.arch.annotation.FragmentContainerParam\n\nprivate val factories by lazy {\n    mutableMapOf<Class<out QMUISchemeFragmentFactory>, QMUISchemeFragmentFactory>()\n}\n\ninternal class FragmentSchemeItem(\n    private val fragmentCls: Class<out QMUIFragment?>,\n    useRefreshIfMatchedCurrent: Boolean,\n    private val activityClsList: Array<Class<out QMUIFragmentActivity>>,\n    private val fragmentFactoryCls: Class<out QMUISchemeFragmentFactory>?,\n    private val forceNewActivity: Boolean,\n    required: ArrayMap<String, String?>?,\n    keysForInt: Array<String>?,\n    keysForBool: Array<String>?,\n    keysForLong: Array<String>?,\n    keysForFloat: Array<String>?,\n    keysForDouble: Array<String>?,\n    defaultParams: Array<String>?,\n    schemeMatcherCls: Class<out QMUISchemeMatcher>?,\n    schemeValueConverterCls: Class<out QMUISchemeValueConverter>?\n) : SchemeItem(\n    required, useRefreshIfMatchedCurrent, keysForInt, keysForBool, keysForLong,\n    keysForFloat, keysForDouble, defaultParams, schemeMatcherCls, schemeValueConverterCls\n) {\n    override fun handle(\n        handler: QMUISchemeHandler,\n        handleContext: SchemeHandleContext,\n        schemeInfo: SchemeInfo\n    ): Boolean {\n        if (activityClsList.isEmpty()) {\n            QMUILog.d(QMUISchemeHandler.TAG, \"Can not start a new fragment because the host is't provided\")\n            return false\n        }\n\n        var factoryCls = fragmentFactoryCls\n        if (factoryCls == null) {\n            factoryCls = handler.defaultFragmentFactory\n        }\n        var factory = factories[factoryCls]\n        if (factory == null) {\n            try {\n                factory = factoryCls.newInstance()\n                factories[factoryCls] = factory\n            } catch (e: Exception) {\n                QMUILog.printErrStackTrace(\n                    QMUISchemeHandler.TAG, e,\n                    \"error to instance QMUISchemeFragmentFactory: %d\", factoryCls.simpleName\n                )\n            }\n        }\n        if (factory == null) {\n            return false\n        }\n        val params = convertFrom(schemeInfo.params)\n        if (factory.shouldBlockJump(handleContext.activity, fragmentCls, params)) {\n            return false\n        }\n        val bundle = factory.factory(params, schemeInfo.origin)\n        if (!isCurrentActivityCanStartFragment(handleContext, params) || isForceNewActivity(params)) {\n            val ret = handleContext.flushAndBuildFirstFragment(activityClsList, params, FragmentAndArg(fragmentCls, bundle, factory))\n            if (ret) {\n                if (shouldFinishCurrent(params)) {\n                    handleContext.shouldFinishCurrent = true\n                }\n                return true\n            }\n            return false\n        }\n        if (handleContext.canUseRefresh() && isUseRefreshIfMatchedCurrent) {\n            val fragmentActivity = handleContext.activity as QMUIFragmentActivity\n            val currentFragment = fragmentActivity.currentFragment\n            if (currentFragment != null && currentFragment.javaClass == fragmentCls && currentFragment is FragmentSchemeRefreshable) {\n                currentFragment.refreshFromScheme(bundle)\n                return true\n            }\n        }\n        handleContext.pushFragment(FragmentAndArg(fragmentCls, bundle, factory))\n        if (shouldFinishCurrent(params)) {\n            handleContext.shouldFinishCurrent = true\n        }\n        return true\n    }\n\n    private fun isCurrentActivityCanStartFragment(handleContext: SchemeHandleContext, scheme: Map<String, SchemeValue>?): Boolean {\n        if (handleContext.intentList.isNotEmpty() || handleContext.buildingIntent != null) {\n            if (!QMUIFragmentActivity::class.java.isAssignableFrom(handleContext.buildingActivityClass)) {\n                return false\n            }\n            val buildingIntent = handleContext.buildingIntent ?: return false\n            for (cls in activityClsList) {\n                if (isCurrentActivityCanStartFragment(\n                        handleContext.buildingActivityClass,\n                        buildingIntent,\n                        cls,\n                        scheme\n                    )\n                ) {\n                    return true\n                }\n            }\n            return false\n        }\n        if (handleContext.activity !is QMUIFragmentActivity) {\n            return false\n        }\n        if (handleContext.activity.supportFragmentManager.isStateSaved) {\n            // use new activity if the state has already been saved.\n            return false\n        }\n        for (cls in activityClsList) {\n            if (isCurrentActivityCanStartFragment(\n                    handleContext.buildingActivityClass,\n                    handleContext.activity.intent,\n                    cls,\n                    scheme\n                )\n            ) {\n                return true\n            }\n        }\n        return false\n    }\n\n    private fun isCurrentActivityCanStartFragment(\n        buildingActivity: Class<out Activity>,\n        buildingIntent: Intent,\n        targetActivity: Class<out QMUIFragmentActivity>,\n        scheme: Map<String, SchemeValue>?\n    ): Boolean {\n        if (!targetActivity.isAssignableFrom(buildingActivity)) {\n            return false\n        }\n        val fragmentContainerParam = targetActivity.getAnnotation(FragmentContainerParam::class.java) ?: return true\n        val required: Array<String> = fragmentContainerParam.required\n        val any: Array<String> = fragmentContainerParam.any\n        if (required.isEmpty() && any.isEmpty()) {\n            return true\n        }\n        if (scheme == null || scheme.isEmpty()) {\n            return false\n        }\n        for (s in required) {\n            val value = scheme[s]\n            if (value == null || !buildingIntent.hasExtra(s)) {\n                return false\n            }\n            if (value.type == java.lang.Boolean.TYPE) {\n                if (buildingIntent.getBooleanExtra(s, false) != value.value as Boolean) {\n                    return false\n                }\n            } else if (value.type == Integer.TYPE) {\n                if (buildingIntent.getIntExtra(s, 0) != value.value as Int) {\n                    return false\n                }\n            } else if (value.type == java.lang.Long.TYPE) {\n                if (buildingIntent.getLongExtra(s, 0) != value.value as Long) {\n                    return false\n                }\n            } else if (value.type == java.lang.Float.TYPE) {\n                if (buildingIntent.getFloatExtra(s, 0f) != value.value as Float) {\n                    return false\n                }\n            } else if (value.type == java.lang.Double.TYPE) {\n                if (buildingIntent.getDoubleExtra(s, 0.0) != value.value as Double) {\n                    return false\n                }\n            } else if (buildingIntent.getStringExtra(s) != value.value) {\n                return false\n            }\n        }\n        for (s in any) {\n            if (buildingIntent.hasExtra(s)) {\n                val value = scheme[s] ?: return false\n                if (value.type == java.lang.Boolean.TYPE) {\n                    if (buildingIntent.getBooleanExtra(s, false) != value.value as Boolean) {\n                        return false\n                    }\n                } else if (value.type == Integer.TYPE) {\n                    if (buildingIntent.getIntExtra(s, 0) != value.value as Int) {\n                        return false\n                    }\n                } else if (value.type == java.lang.Long.TYPE) {\n                    if (buildingIntent.getLongExtra(s, 0) != value.value as Long) {\n                        return false\n                    }\n                } else if (value.type == java.lang.Float.TYPE) {\n                    if (buildingIntent.getFloatExtra(s, 0f) != value.value as Float) {\n                        return false\n                    }\n                } else if (value.type == java.lang.Double.TYPE) {\n                    if (buildingIntent.getDoubleExtra(s, 0.0) != value.value as Double) {\n                        return false\n                    }\n                } else if (buildingIntent.getStringExtra(s) != value.value) {\n                    return false\n                }\n            }\n        }\n        return true\n    }\n\n    private fun isForceNewActivity(scheme: Map<String, SchemeValue>?): Boolean {\n        if (forceNewActivity) {\n            return true\n        }\n        if (scheme == null || scheme.isEmpty()) {\n            return false\n        }\n        val schemeValue = scheme[QMUISchemeHandler.ARG_FORCE_TO_NEW_ACTIVITY]\n        return schemeValue != null && schemeValue.type == java.lang.Boolean.TYPE && (schemeValue.value as Boolean)\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeBuilder.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.net.Uri\nimport android.util.ArrayMap\nimport java.util.*\n\nclass QMUISchemeBuilder(\n    private val prefix: String,\n    private val action: String,\n    private val encodeParams: Boolean\n) {\n\n    companion object {\n        fun from(prefix: String, action: String, params: String?, encodeNewParams: Boolean): QMUISchemeBuilder {\n            val builder = QMUISchemeBuilder(prefix, action, encodeNewParams)\n            val paramsMap = HashMap<String, String>()\n            parseParamsToMap(params, paramsMap)\n            if (paramsMap.isNotEmpty()) {\n                builder.params.putAll(paramsMap)\n            }\n            return builder\n        }\n    }\n\n    private val params = ArrayMap<String, String>()\n\n    fun param(name: String, value: String): QMUISchemeBuilder {\n        if (encodeParams) {\n            params[name] = Uri.encode(value)\n        } else {\n            params[name] = value\n        }\n        return this\n    }\n\n    fun param(name: String, value: Int): QMUISchemeBuilder {\n        params[name] = value.toString()\n        return this\n    }\n\n    fun param(name: String, value: Boolean): QMUISchemeBuilder {\n        params[name] = if (value) \"1\" else \"0\"\n        return this\n    }\n\n    fun param(name: String, value: Long): QMUISchemeBuilder {\n        params[name] = value.toString()\n        return this\n    }\n\n    fun param(name: String, value: Float): QMUISchemeBuilder {\n        params[name] = value.toString()\n        return this\n    }\n\n    fun param(name: String, value: Double): QMUISchemeBuilder {\n        params[name] = value.toString()\n        return this\n    }\n\n    fun finishCurrent(finishCurrent: Boolean): QMUISchemeBuilder {\n        params[QMUISchemeHandler.ARG_FINISH_CURRENT] = if (finishCurrent) \"1\" else \"0\"\n        return this\n    }\n\n    fun forceToNewActivity(forceNew: Boolean): QMUISchemeBuilder {\n        params[QMUISchemeHandler.ARG_FORCE_TO_NEW_ACTIVITY] = if (forceNew) \"1\" else \"0\"\n        return this\n    }\n\n    fun build(): String {\n        val builder = StringBuilder()\n        builder.append(prefix)\n        builder.append(action)\n        builder.append(\"?\")\n        for (i in 0 until params.size) {\n            if (i != 0) {\n                builder.append(\"&\")\n            }\n            builder.append(params.keyAt(i))\n            builder.append(\"=\")\n            builder.append(params.valueAt(i))\n        }\n        return builder.toString()\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeFragmentFactory.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport com.qmuiteam.qmui.QMUILog\nimport com.qmuiteam.qmui.arch.QMUIFragment\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity\nimport com.qmuiteam.qmui.arch.R\n\ninterface QMUISchemeFragmentFactory {\n    fun factory(fragmentCls: Class<out QMUIFragment>, bundle: Bundle?): QMUIFragment?\n    fun factory(scheme: Map<String, SchemeValue>?, origin: String): Bundle?\n    fun proxy(intent: Intent): Intent\n\n    fun startActivities(activity: Activity, intent: List<Intent>, schemeInfo: List<SchemeInfo>)\n    fun startFragmentAndDestroyCurrent(activity: QMUIFragmentActivity, fragment: QMUIFragment, schemeInfo: SchemeInfo): Int\n    fun startFragment(activity: QMUIFragmentActivity, fragment: List<QMUIFragment>, schemeInfo: List<SchemeInfo>): Int\n    fun shouldBlockJump(\n        activity: Activity,\n        fragmentCls: Class<out QMUIFragment>,\n        scheme: Map<String, SchemeValue>?\n    ): Boolean\n}\n\n\nopen class QMUIDefaultSchemeFragmentFactory : QMUISchemeFragmentFactory {\n    override fun factory(\n        fragmentCls: Class<out QMUIFragment>,\n        bundle: Bundle?\n    ): QMUIFragment? {\n        return try {\n            val fragment = fragmentCls.newInstance()\n            fragment.arguments = bundle\n            fragment\n        } catch (e: Exception) {\n            QMUILog.printErrStackTrace(\n                QMUISchemeHandler.TAG, e,\n                \"Error to create fragment: %s\", fragmentCls.simpleName\n            )\n            null\n        }\n    }\n\n    override fun factory(scheme: Map<String, SchemeValue>?, origin: String): Bundle? {\n        val bundle = Bundle()\n        bundle.putBoolean(QMUISchemeHandler.ARG_FROM_SCHEME, true)\n        bundle.putString(QMUISchemeHandler.ARG_ORIGIN_SCHEME, origin)\n        if (scheme != null && scheme.isNotEmpty()) {\n            for ((name, schemeValue) in scheme) {\n                when (schemeValue.type) {\n                    Integer.TYPE -> bundle.putInt(name, schemeValue.value as Int)\n                    java.lang.Boolean.TYPE -> bundle.putBoolean(name, schemeValue.value as Boolean)\n                    java.lang.Long.TYPE -> bundle.putLong(name, schemeValue.value as Long)\n                    java.lang.Float.TYPE -> bundle.putFloat(name, schemeValue.value as Float)\n                    java.lang.Double.TYPE -> bundle.putDouble(name, schemeValue.value as Double)\n                    else -> bundle.putString(name, schemeValue.origin)\n                }\n            }\n        }\n        return bundle\n    }\n\n    override fun proxy(intent: Intent): Intent {\n        return intent\n    }\n\n    override fun startActivities(activity: Activity, intent: List<Intent>, schemeInfo: List<SchemeInfo>) {\n        if (intent.size == 1) {\n            activity.startActivity(intent[0])\n        } else {\n            activity.startActivities(intent.toTypedArray())\n        }\n    }\n\n    override fun startFragmentAndDestroyCurrent(activity: QMUIFragmentActivity, fragment: QMUIFragment, schemeInfo: SchemeInfo): Int {\n        return activity.startFragmentAndDestroyCurrent(fragment, true)\n    }\n\n    override fun startFragment(activity: QMUIFragmentActivity, fragment: List<QMUIFragment>, schemeInfo: List<SchemeInfo>): Int {\n        return activity.startFragments(fragment)\n    }\n\n    override fun shouldBlockJump(\n        activity: Activity,\n        fragmentCls: Class<out QMUIFragment>,\n        scheme: Map<String, SchemeValue>?\n    ): Boolean {\n        return false\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeHandler.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport com.qmuiteam.qmui.QMUILog\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity\nimport com.qmuiteam.qmui.arch.QMUISwipeBackActivityManager\nimport java.util.*\n\nclass QMUISchemeHandler private constructor(builder: Builder) {\n    companion object {\n        const val TAG = \"QMUISchemeHandler\"\n        const val ARG_FROM_SCHEME = \"__qmui_arg_from_scheme\"\n        const val ARG_ORIGIN_SCHEME = \"__qmui_arg_origin_scheme\"\n        const val ARG_FORCE_TO_NEW_ACTIVITY = \"__qmui_force_to_new_activity\"\n        const val ARG_FINISH_CURRENT = \"__qmui_finish_current\"\n        private var sSchemeMap: SchemeMap? = null\n\n        init {\n            try {\n                val cls = Class.forName(SchemeMap::class.java.name + \"Impl\")\n                sSchemeMap = cls.newInstance() as SchemeMap\n            } catch (e: ClassNotFoundException) {\n                sSchemeMap = object : SchemeMap {\n                    override fun findScheme(handler: QMUISchemeHandler, schemeAction: String, params: Map<String, String>?): SchemeItem? {\n                        return null\n                    }\n\n                    override fun exists(handler: QMUISchemeHandler, schemeAction: String): Boolean {\n                        return false\n                    }\n                }\n            } catch (e: IllegalAccessException) {\n                throw RuntimeException(\n                    \"Can not access the Class SchemeMapImpl. \" +\n                            \"Please file a issue to report this.\"\n                )\n            } catch (e: InstantiationException) {\n                throw RuntimeException(\n                    \"Can not instance the Class SchemeMapImpl. \" +\n                            \"Please file a issue to report this.\"\n                )\n            }\n        }\n    }\n\n    val prefix: String = builder.prefix\n    private var interpolatorList: List<QMUISchemeHandlerInterceptor> = builder.interceptorList\n    private val blockSameSchemeTimeout = builder.blockSameSchemeTimeout\n    val defaultIntentFactory = builder.defaultIntentFactory\n    val defaultFragmentFactory = builder.defaultFragmentFactory\n    val defaultSchemeMatcher = builder.defaultSchemeMatcher\n    private val fallbackInterceptor = builder.fallbackInterceptor\n    private val unKnownSchemeHandler = builder.unKnownSchemeHandler\n    private var lastHandledScheme: List<String>? = null\n    private var lastSchemeHandledTime: Long = 0\n\n\n    fun getSchemeItem(action: String, params: Map<String, String>?): SchemeItem? {\n        return sSchemeMap?.findScheme(this, action, params)\n    }\n\n    fun handle(scheme: String): Boolean {\n        val list = ArrayList<String>(1)\n        list.add(scheme)\n        return handleSchemes(list)\n    }\n\n    fun handleSchemes(schemes: List<String>): Boolean {\n        if (schemes.isEmpty()) {\n            return false\n        }\n        for (scheme in schemes) {\n            if (!scheme.startsWith(prefix)) {\n                return false\n            }\n        }\n        if (schemes == lastHandledScheme && System.currentTimeMillis() - lastSchemeHandledTime < blockSameSchemeTimeout) {\n            return true\n        }\n        val currentActivity = QMUISwipeBackActivityManager.getInstance().currentActivity ?: return false\n        val schemeInfoList = ArrayList<SchemeInfo>(schemes.size)\n        for (schemeParam in schemes) {\n            val scheme = schemeParam.substring(prefix.length)\n            val elements: Array<String?> = scheme.split(\"\\\\?\".toRegex()).toTypedArray()\n            val action = elements[0]\n            if (elements.isEmpty() || action == null || action.isEmpty()) {\n                return false\n            }\n            val params = mutableMapOf<String, String>()\n            if (elements.size > 1) {\n                parseParamsToMap(elements[1], params)\n            }\n            schemeInfoList.add(SchemeInfo(action, params, scheme))\n        }\n        var handled = false\n        if (interpolatorList.isNotEmpty()) {\n            for (interpolator in interpolatorList) {\n                if (interpolator.intercept(this, currentActivity, schemeInfoList)) {\n                    handled = true\n                    break\n                }\n            }\n        }\n        if (!handled) {\n            var failed = false\n            val handleContext = SchemeHandleContext(currentActivity)\n            for (schemeInfo in schemeInfoList) {\n                val schemeItem = sSchemeMap!!.findScheme(this, schemeInfo.action, schemeInfo.params)\n                if (schemeItem == null) {\n                    QMUILog.i(TAG, \"findScheme failed: ${schemeInfo.origin}\")\n                    if(unKnownSchemeHandler != null && unKnownSchemeHandler.handle(this, handleContext, schemeInfo)){\n                        continue\n                    }\n                    failed = true\n                    break\n                }\n                schemeItem.appendDefaultParams(schemeInfo.params)\n                if (!schemeItem.handle(this, handleContext, schemeInfo)) {\n                    QMUILog.i(TAG, \"handle scheme failed: ${schemeInfo.origin}\")\n                    failed = true\n                    break\n                }\n            }\n            if (!failed) {\n                val fragmentList = handleContext.fragmentList\n                val buildingIntent = handleContext.buildingIntent\n                if (handleContext.intentList.isEmpty() && buildingIntent == null) {\n                    val fragments = fragmentList.mapNotNull {\n                        it.factory.factory(it.fragmentClass, it.arg)\n                    }\n                    if (fragments.size == fragmentList.size) {\n                        if (handleContext.shouldFinishCurrent) {\n                            if (fragmentList.size == 1) {\n                                fragmentList.last().factory.startFragmentAndDestroyCurrent(\n                                    handleContext.activity as QMUIFragmentActivity, fragments[0], schemeInfoList[0]\n                                )\n                                handled = true\n                            } else {\n                                QMUILog.e(TAG, \"startFragmentAndDestroyCurrent not support muti fragments\")\n                            }\n                        } else {\n                            val commitId =\n                                fragmentList.last().factory.startFragment(handleContext.activity as QMUIFragmentActivity, fragments, schemeInfoList)\n                            handled = commitId >= 0\n                        }\n                    }\n                } else {\n                    handled = handleContext.startActivities(schemeInfoList)\n                    if (handled && handleContext.shouldFinishCurrent) {\n                        handleContext.activity.finish()\n                    }\n                }\n            }\n        }\n        if (!handled && fallbackInterceptor != null) {\n            handled = fallbackInterceptor.intercept(this, currentActivity, schemeInfoList)\n        }\n        if (handled) {\n            lastHandledScheme = schemes\n            lastSchemeHandledTime = System.currentTimeMillis()\n        }\n        return handled\n    }\n\n    class Builder(val prefix: String) {\n        val interceptorList = mutableListOf<QMUISchemeHandlerInterceptor>()\n\n        var blockSameSchemeTimeout = BLOCK_SAME_SCHEME_DEFAULT_TIMEOUT\n        var defaultIntentFactory: Class<out QMUISchemeIntentFactory> = QMUIDefaultSchemeIntentFactory::class.java\n        var defaultFragmentFactory: Class<out QMUISchemeFragmentFactory> = QMUIDefaultSchemeFragmentFactory::class.java\n        var defaultSchemeMatcher: Class<out QMUISchemeMatcher> = QMUIDefaultSchemeMatcher::class.java\n        var unKnownSchemeHandler: QMUIUnknownSchemeHandler? = null\n        var fallbackInterceptor: QMUISchemeHandlerInterceptor? = null\n\n        fun addInterceptor(interceptor: QMUISchemeHandlerInterceptor) {\n            interceptorList.add(interceptor)\n        }\n\n        fun build(): QMUISchemeHandler {\n            return QMUISchemeHandler(this)\n        }\n\n        companion object {\n            const val BLOCK_SAME_SCHEME_DEFAULT_TIMEOUT: Long = 500\n        }\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeHandlerInterceptor.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.net.Uri\n\nfun interface QMUISchemeHandlerInterceptor {\n\n    fun intercept(\n        schemeHandler: QMUISchemeHandler,\n        activity: Activity,\n        schemes: List<SchemeInfo>\n    ): Boolean\n}\n\n\nclass QMUISchemeParamValueDecoder : QMUISchemeHandlerInterceptor {\n    override fun intercept(\n        schemeHandler: QMUISchemeHandler,\n        activity: Activity,\n        schemes: List<SchemeInfo>\n    ): Boolean {\n        for (scheme in schemes) {\n            for ((key, value) in scheme.params) {\n                if (value.isNotBlank()) {\n                    scheme.params[key] = Uri.decode(value)\n                }\n            }\n        }\n        return false\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeIntentFactory.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.content.Intent\n\ninterface QMUISchemeIntentFactory {\n    fun factory(\n        activity: Activity,\n        activityClass: Class<out Activity>,\n        scheme: Map<String, SchemeValue>?,\n        origin: String\n    ): Intent?\n\n    fun startActivities(\n        activity: Activity,\n        intent: List<Intent>,\n        schemeInfo: List<SchemeInfo>\n    )\n\n    fun shouldBlockJump(\n        activity: Activity,\n        activityClass: Class<out Activity>,\n        scheme: Map<String, SchemeValue>?\n    ): Boolean\n}\n\n\nopen class QMUIDefaultSchemeIntentFactory : QMUISchemeIntentFactory {\n    override fun factory(\n        activity: Activity,\n        activityClass: Class<out Activity>,\n        scheme: Map<String, SchemeValue>?,\n        origin: String\n    ): Intent {\n        val intent = Intent(activity, activityClass)\n        intent.putExtra(QMUISchemeHandler.ARG_FROM_SCHEME, true)\n        intent.putExtra(QMUISchemeHandler.ARG_ORIGIN_SCHEME, origin)\n        if (scheme != null && scheme.isNotEmpty()) {\n            for ((name, schemeValue) in scheme) {\n                when (schemeValue.type) {\n                    Integer.TYPE -> intent.putExtra(name, schemeValue.value as Int)\n                    java.lang.Boolean.TYPE -> intent.putExtra(name, schemeValue.value as Boolean)\n                    java.lang.Long.TYPE -> intent.putExtra(name, schemeValue.value as Long)\n                    java.lang.Float.TYPE -> intent.putExtra(name, schemeValue.value as Float)\n                    java.lang.Double.TYPE -> intent.putExtra(name, schemeValue.value as Double)\n                    else -> intent.putExtra(name, schemeValue.origin)\n                }\n            }\n        }\n        return intent\n    }\n\n    override fun startActivities(activity: Activity, intent: List<Intent>, schemeInfo: List<SchemeInfo>) {\n        if (intent.size == 1) {\n            activity.startActivity(intent[0])\n        } else {\n            activity.startActivities(intent.toTypedArray())\n        }\n    }\n\n    override fun shouldBlockJump(\n        activity: Activity,\n        activityClass: Class<out Activity?>,\n        scheme: Map<String, SchemeValue>?\n    ): Boolean {\n        return false\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUISchemeMatcher.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\ninterface QMUISchemeMatcher {\n    fun match(schemeItem: SchemeItem, params: Map<String, String?>?): Boolean\n}\n\nopen class QMUIDefaultSchemeMatcher : QMUISchemeMatcher {\n    override fun match(schemeItem: SchemeItem, params: Map<String, String?>?): Boolean {\n        return schemeItem.matchRequiredParam(params)\n    }\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/QMUIUnknownSchemeHandler.kt",
    "content": "package com.qmuiteam.qmui.arch.scheme\n\ninterface QMUIUnknownSchemeHandler {\n    fun handle(handler: QMUISchemeHandler, handleContext: SchemeHandleContext, schemeInfo: SchemeInfo): Boolean\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeHandleContext.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport com.qmuiteam.qmui.arch.QMUIFragment\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity\nimport com.qmuiteam.qmui.arch.annotation.FragmentContainerParam\nimport java.util.*\n\nclass SchemeHandleContext(val activity: Activity) {\n\n    val intentList: MutableList<Intent> = ArrayList()\n    val fragmentList: MutableList<FragmentAndArg> = ArrayList()\n\n    var buildingIntent: Intent? = null\n    var buildingActivityClass: Class<out Activity> = activity::class.java\n    var shouldFinishCurrent = false\n\n    private var schemeIntentFactory: QMUISchemeIntentFactory? = null\n    private var schemeFragmentFactory: QMUISchemeFragmentFactory? = null\n\n    fun startActivities(schemeInfo: List<SchemeInfo>): Boolean {\n        flushFragment()\n        if (intentList.isEmpty()) {\n            return false\n        }\n        intentList.forEachIndexed { index, intent ->\n            intent.putExtra(QMUIFragmentActivity.QMUI_MUTI_START_INDEX, index)\n        }\n        schemeFragmentFactory?.let {\n            it.startActivities(activity, intentList, schemeInfo)\n            return true\n        }\n        schemeIntentFactory?.let {\n            it.startActivities(activity, intentList, schemeInfo)\n            return true\n        }\n        return false\n    }\n\n    fun canUseRefresh(): Boolean {\n        return intentList.isEmpty() && fragmentList.isEmpty()\n    }\n\n    fun pushActivity(cls: Class<out Activity>, intent: Intent, factory: QMUISchemeIntentFactory) {\n        flushFragment()\n        intentList.add(intent)\n        schemeIntentFactory = factory\n        schemeFragmentFactory = null\n        buildingActivityClass = cls\n    }\n\n    private fun flushFragment() {\n        if (fragmentList.isNotEmpty()) {\n            val intent = buildingIntent ?: Intent(activity, buildingActivityClass).apply {\n                putExtras(activity.intent)\n            }.let {\n                fragmentList.first().factory.proxy(it)\n            }\n            val fragmentListArg = arrayListOf<Bundle>()\n            fragmentList.forEach {\n                fragmentListArg.add(Bundle().apply {\n                    putString(QMUIFragmentActivity.QMUI_INTENT_DST_FRAGMENT_NAME, it.fragmentClass.name)\n                    putBundle(QMUIFragmentActivity.QMUI_INTENT_FRAGMENT_ARG, it.arg)\n                })\n            }\n            intent.putParcelableArrayListExtra(QMUIFragmentActivity.QMUI_INTENT_FRAGMENT_LIST_ARG, fragmentListArg)\n            intentList.add(intent)\n            buildingIntent = null\n            fragmentList.clear()\n        }\n    }\n\n    fun flushAndBuildFirstFragment(\n        activityClsList: Array<Class<out QMUIFragmentActivity>>,\n        params: Map<String, SchemeValue>?,\n        fragmentAndArg: FragmentAndArg\n    ): Boolean {\n        flushFragment()\n        for (target in activityClsList) {\n            val intent = buildIntentForFragment(target, params)\n            if (intent != null) {\n                buildingIntent = fragmentAndArg.factory.proxy(intent)\n                buildingActivityClass = target\n                pushFragment(fragmentAndArg)\n                return true\n            }\n        }\n        return false\n    }\n\n\n    fun pushFragment(fragmentAndArg: FragmentAndArg) {\n        fragmentList.add(fragmentAndArg)\n        schemeIntentFactory = null\n        schemeFragmentFactory = fragmentAndArg.factory\n    }\n\n    private fun buildIntentForFragment(\n        activityCls: Class<out QMUIFragmentActivity>,\n        params: Map<String, SchemeValue>?\n    ): Intent? {\n        val intent = Intent(activity, activityCls)\n        intent.putExtra(QMUISchemeHandler.ARG_FROM_SCHEME, true)\n        val fragmentContainerParam = activityCls.getAnnotation(FragmentContainerParam::class.java) ?: return intent\n        val required: Array<String> = fragmentContainerParam.required\n        val any: Array<String> = fragmentContainerParam.any\n        val optional: Array<String> = fragmentContainerParam.optional\n        if (required.isEmpty() && any.isEmpty()) {\n            putOptionalSchemeValuesToIntent(intent, params, optional)\n            return intent\n        }\n        if (params == null || params.isEmpty()) {\n            // not matched.\n            return null\n        }\n        if (required.isNotEmpty()) {\n            for (arg in required) {\n                val value = params[arg] ?: return null // not matched.\n                putSchemeValueToIntent(intent, arg, value)\n            }\n        }\n        if (any.isNotEmpty()) {\n            var hasAny = false\n            for (arg in any) {\n                val value = params[arg]\n                if (value != null) {\n                    putSchemeValueToIntent(intent, arg, value)\n                    hasAny = true\n                }\n            }\n            if (!hasAny) {\n                return null\n            }\n        }\n        putOptionalSchemeValuesToIntent(intent, params, optional)\n        return intent\n    }\n\n\n    private fun putOptionalSchemeValuesToIntent(\n        intent: Intent,\n        scheme: Map<String, SchemeValue>?,\n        optional: Array<String>\n    ) {\n        if (scheme == null || scheme.isEmpty()) {\n            return\n        }\n        for (arg in optional) {\n            val value = scheme[arg]\n            value?.let { putSchemeValueToIntent(intent, arg, it) }\n        }\n    }\n\n    private fun putSchemeValueToIntent(intent: Intent, arg: String, value: SchemeValue) {\n        when (value.type) {\n            java.lang.Boolean.TYPE -> intent.putExtra(arg, value.value as Boolean)\n            Integer.TYPE -> intent.putExtra(arg, value.value as Int)\n            java.lang.Long.TYPE -> intent.putExtra(arg, value.value as Long)\n            java.lang.Float.TYPE -> intent.putExtra(arg, value.value as Float)\n            java.lang.Double.TYPE -> intent.putExtra(arg, value.value as Double)\n            else -> intent.putExtra(arg, value.origin)\n        }\n    }\n}\n\nclass FragmentAndArg(\n    val fragmentClass: Class<out QMUIFragment>,\n    val arg: Bundle?,\n    val factory: QMUISchemeFragmentFactory\n)\n"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeInfo.kt",
    "content": "package com.qmuiteam.qmui.arch.scheme\n\nclass SchemeInfo(\n    val action: String,\n    val params: MutableMap<String, String>,\n    val origin: String\n)\n\n\nfun parseParamsToMap(schemeParams: String?, queryMap: MutableMap<String, String>) {\n    if (schemeParams == null || schemeParams.isEmpty()) {\n        return\n    }\n    var start = 0\n    do {\n        val next = schemeParams.indexOf('&', start)\n        val end = if (next == -1) schemeParams.length else next\n        if (start == end) {\n            start += 1\n            continue\n        }\n        var separator = schemeParams.indexOf('=', start)\n        if (separator > end || separator == -1) {\n            separator = end\n        }\n        if (separator == start) {\n            start = end + 1\n            continue\n        }\n        val name = schemeParams.substring(start, separator)\n        val value = if (separator == end) \"\" else schemeParams.substring(separator + 1, end)\n        queryMap[name] = value\n        start = end + 1\n    } while (start < schemeParams.length)\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeItem.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.util.ArrayMap\nimport com.qmuiteam.qmui.QMUILog\nimport java.util.*\n\nprivate val schemeMatchers by lazy {\n    HashMap<Class<out QMUISchemeMatcher>, QMUISchemeMatcher>()\n}\nprivate val schemeValueConverters by lazy {\n    HashMap<Class<out QMUISchemeValueConverter>, QMUISchemeValueConverter>()\n}\n\nabstract class SchemeItem(\n    private val required: ArrayMap<String, String?>?,\n    val isUseRefreshIfMatchedCurrent: Boolean,\n    private val keysForInt: Array<String>?,\n    private val keysForBool: Array<String>?,\n    private val keysForLong: Array<String>?,\n    private val keysForFloat: Array<String>?,\n    private val keysForDouble: Array<String>?,\n    private val defaultParams: Array<String>?,\n    private val schemeMatcherCls: Class<out QMUISchemeMatcher>?,\n    private val schemeValueConverterCls: Class<out QMUISchemeValueConverter>?\n) {\n\n    fun appendDefaultParams(schemeParams: MutableMap<String, String>?) {\n        if(schemeParams == null || defaultParams == null){\n            return\n        }\n        for (item in defaultParams) {\n            if (item.isNotEmpty()) {\n                val pair = item.split(\"=\")\n                if (pair.size == 2) {\n                    if(!schemeParams.contains(pair[0])){\n                        schemeParams[pair[0]] = pair[1]\n                    }\n                }\n            }\n        }\n    }\n\n    protected fun convertFrom(schemeParams: Map<String, String>?): Map<String, SchemeValue>? {\n\n        if (schemeParams == null || schemeParams.isEmpty()) {\n            return null\n        }\n        val queryMap = mutableMapOf<String, SchemeValue>()\n        for ((name, value) in schemeParams) {\n            if (name.isEmpty()) {\n                continue\n            }\n            var usedValue = value\n            if (schemeValueConverterCls != null) {\n                var converter = schemeValueConverters[schemeValueConverterCls]\n                if (converter == null) {\n                    try {\n                        converter = schemeValueConverterCls.newInstance()\n                        schemeValueConverters[schemeValueConverterCls] = converter\n                    } catch (e: Exception) {\n                        QMUILog.printErrStackTrace(\n                            QMUISchemeHandler.TAG, e,\n                            \"error to instance QMUISchemeValueConverter: %d\", schemeValueConverterCls.simpleName\n                        )\n                    }\n                }\n                if (converter != null) {\n                    usedValue = converter.convert(name, value, schemeParams)\n                }\n            }\n            try {\n                when {\n                    keysForInt?.contains(name) == true -> {\n                        queryMap[name] = SchemeValue(usedValue, Integer.valueOf(usedValue), Integer.TYPE)\n                    }\n                    isBoolKey(name) -> {\n                        queryMap[name] = SchemeValue(usedValue, convertStringToBool(usedValue), java.lang.Boolean.TYPE)\n                    }\n                    keysForLong?.contains(name) == true -> {\n                        queryMap[name] = SchemeValue(usedValue, java.lang.Long.valueOf(usedValue), java.lang.Long.TYPE)\n                    }\n                    keysForFloat?.contains(name) == true -> {\n                        queryMap[name] = SchemeValue(usedValue, java.lang.Float.valueOf(usedValue), java.lang.Float.TYPE)\n                    }\n                    keysForDouble?.contains(name) == true -> {\n                        queryMap[name] = SchemeValue(usedValue, java.lang.Double.valueOf(usedValue), java.lang.Double.TYPE)\n                    }\n                    else -> {\n                        queryMap[name] = SchemeValue(usedValue, usedValue, String::class.java)\n                    }\n                }\n            } catch (e: Exception) {\n                QMUILog.printErrStackTrace(QMUISchemeHandler.TAG, e, \"error to parse scheme param: %s = %s\", name, value)\n            }\n        }\n        return queryMap\n    }\n\n    private fun isBoolKey(name: String): Boolean {\n        return QMUISchemeHandler.ARG_FORCE_TO_NEW_ACTIVITY == name || QMUISchemeHandler.ARG_FINISH_CURRENT == name ||\n                keysForBool?.contains(name) == true\n    }\n\n    private fun convertStringToBool(text: String?): Boolean {\n        return !(text.isNullOrBlank() || \"0\" == text || \"false\" == text.lowercase())\n    }\n\n    protected fun shouldFinishCurrent(scheme: Map<String, SchemeValue>?): Boolean {\n        if (scheme == null || scheme.isEmpty()) {\n            return false\n        }\n        val schemeValue = scheme[QMUISchemeHandler.ARG_FINISH_CURRENT]\n        return schemeValue != null && schemeValue.type == java.lang.Boolean.TYPE && schemeValue.value as Boolean\n    }\n\n    private fun getSchemeMatcher(handler: QMUISchemeHandler): QMUISchemeMatcher? {\n        var schemeMatcherCls = schemeMatcherCls\n        if (schemeMatcherCls == null) {\n            schemeMatcherCls = handler.defaultSchemeMatcher\n        }\n        var matcher = schemeMatchers[schemeMatcherCls]\n        if (matcher == null) {\n            try {\n                matcher = schemeMatcherCls.newInstance()\n                schemeMatchers[schemeMatcherCls] = matcher\n            } catch (e: Exception) {\n                QMUILog.printErrStackTrace(\n                    QMUISchemeHandler.TAG, e,\n                    \"error to instance QMUISchemeMatcher: %d\", schemeMatcherCls.simpleName\n                )\n            }\n        }\n        return matcher\n    }\n\n    // used by generated code(SchemeMapImpl)\n    fun match(handler: QMUISchemeHandler, params: Map<String, String?>?): Boolean {\n        val matcher = getSchemeMatcher(handler)\n        return matcher?.match(this, params) ?: matchRequiredParam(params)\n    }\n\n    fun matchRequiredParam(params: Map<String, String?>?): Boolean {\n        if (required == null || required.isEmpty()) {\n            return true\n        }\n        if (params == null || params.isEmpty()) {\n            return false\n        }\n        for (i in 0 until required.size) {\n            val key = required.keyAt(i)\n            if (!params.containsKey(key)) {\n                return false\n            }\n            val value = required.valueAt(i)\n                ?: // if no value. that means scheme must provide this key.\n                continue\n            val actual = params[key]\n            if (actual == null || actual != value) {\n                return false\n            }\n        }\n        return true\n    }\n\n    abstract fun handle(handler: QMUISchemeHandler, handleContext: SchemeHandleContext, schemeInfo: SchemeInfo): Boolean\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeMap.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\ninterface SchemeMap {\n    fun findScheme(handler: QMUISchemeHandler, schemeAction: String, params: Map<String, String>?): SchemeItem?\n    fun exists(handler: QMUISchemeHandler, schemeAction: String): Boolean\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeRefreshable.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nimport android.content.Intent\nimport android.os.Bundle\n\ninterface ActivitySchemeRefreshable {\n    fun refreshFromScheme(intent: Intent?)\n}\n\ninterface FragmentSchemeRefreshable {\n    fun refreshFromScheme(bundle: Bundle?)\n}"
  },
  {
    "path": "arch/src/main/java/com/qmuiteam/qmui/arch/scheme/SchemeValue.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.scheme\n\nclass SchemeValue(\n    val origin: String,\n    val value: Any,\n    val type: Class<*>\n)\n\ninterface QMUISchemeValueConverter {\n    fun convert(key: String, originValue: String, schemeParams: Map<String, String?>?): String\n}\n\nclass QMUIDefaultSchemeValueConverter : QMUISchemeValueConverter {\n    override fun convert(key: String, originValue: String, schemeParams: Map<String, String?>?): String {\n        return originValue\n    }\n}"
  },
  {
    "path": "arch/src/main/res/anim/decelerate_factor_interpolator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<decelerateInterpolator\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:factor=\"1.4\" />"
  },
  {
    "path": "arch/src/main/res/anim/decelerate_low_factor_interpolator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<decelerateInterpolator\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:factor=\"1.1\" />"
  },
  {
    "path": "arch/src/main/res/anim/scale_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:background=\"@android:color/transparent\">\n\n    <scale\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXScale=\"0.9\"\n        android:fromYScale=\"0.9\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"1.0\"\n        android:toYScale=\"1.0\" />\n\n    <alpha\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromAlpha=\"0.0\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:toAlpha=\"1.0\" />\n\n</set>"
  },
  {
    "path": "arch/src/main/res/anim/scale_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:background=\"@android:color/transparent\">\n\n    <scale\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"0.8\"\n        android:toYScale=\"0.8\" />\n\n    <alpha\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromAlpha=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:toAlpha=\"0.0\" />\n\n</set>"
  },
  {
    "path": "arch/src/main/res/anim/slide_in_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@anim/decelerate_factor_interpolator\">\n\n    <translate\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXDelta=\"-40%p\"\n        android:toXDelta=\"0%p\" />\n\n    <alpha\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromAlpha=\"0.85\"\n        android:toAlpha=\"1\" />\n \n</set>"
  },
  {
    "path": "arch/src/main/res/anim/slide_in_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@anim/decelerate_factor_interpolator\">\n\n    <translate\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXDelta=\"100%p\"\n        android:toXDelta=\"0%p\"/>\n    \n</set>"
  },
  {
    "path": "arch/src/main/res/anim/slide_out_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@anim/decelerate_low_factor_interpolator\">\n\n    <translate\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXDelta=\"0%p\"\n        android:toXDelta=\"-100%p\" />\n\n    <alpha\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromAlpha=\"1\"\n        android:toAlpha=\"0.85\" />\n    \n</set>"
  },
  {
    "path": "arch/src/main/res/anim/slide_out_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@anim/decelerate_factor_interpolator\">\n\n    <translate\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromXDelta=\"0%p\"\n        android:toXDelta=\"100%p\"/>\n\n</set>"
  },
  {
    "path": "arch/src/main/res/anim/slide_still.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <alpha\n        android:duration=\"@integer/qmui_anim_duration\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quad\" />\n\n</set>"
  },
  {
    "path": "arch/src/main/res/anim/swipe_back_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n           android:duration=\"0\"\n           android:fromXDelta=\"0\"\n           android:fromYDelta=\"0\"\n           android:toXDelta=\"0\"\n           android:toYDelta=\"0\"/>"
  },
  {
    "path": "arch/src/main/res/anim/swipe_back_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- some machines will flash black if the duration == 0 or fromXDelta > 0%p -->\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n           android:duration=\"1\"\n           android:fromXDelta=\"0%p\"\n           android:fromYDelta=\"0\"\n           android:toXDelta=\"100%p\"\n           android:toYDelta=\"0\"/>\n"
  },
  {
    "path": "arch/src/main/res/anim/swipe_back_exit_still.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- when previous activity has a child window, backing to previous activity will run the child\nwindow enter animation, but so far I have no way to reset the enter animation. -->\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n           android:duration=\"@integer/qmui_anim_duration\"\n           android:fromXDelta=\"0%p\"\n           android:fromYDelta=\"0\"\n           android:toXDelta=\"0%p\"\n           android:toYDelta=\"0\"/>\n"
  },
  {
    "path": "arch/src/main/res/animator/scale_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"scaleX\"\n        android:valueFrom=\"0.9\" android:valueTo=\"1.0\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"scaleY\"\n        android:valueFrom=\"0.9\" android:valueTo=\"1.0\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"0.0\" android:valueTo=\"1.0\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/scale_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:background=\"@android:color/transparent\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"scaleX\"\n        android:valueFrom=\"1.0\" android:valueTo=\"0.9\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"scaleY\"\n        android:valueFrom=\"1.0\" android:valueTo=\"0.9\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"1.0\" android:valueTo=\"0.0\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/slide_in_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"xFraction\"\n        android:valueFrom=\"-0.4\" android:valueTo=\"0\"\n        android:valueType=\"floatType\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"0.85\" android:valueTo=\"1.0\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/slide_in_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:propertyName=\"xFraction\"\n        android:valueFrom=\"1.0\" android:valueTo=\"0.0\"\n        android:valueType=\"floatType\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/slide_out_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"xFraction\"\n        android:valueFrom=\"0.0\" android:valueTo=\"-1.0\"\n        android:valueType=\"floatType\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"1.0\" android:valueTo=\"0.85\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/slide_out_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"xFraction\"\n        android:valueFrom=\"0.0\" android:valueTo=\"1.0\"\n        android:valueType=\"floatType\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n</set>"
  },
  {
    "path": "arch/src/main/res/animator/slide_still.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnimator\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:propertyName=\"x\"\n        android:valueFrom=\"0.0\" android:valueTo=\"0.0\"\n        android:valueType=\"floatType\"\n        android:duration=\"@integer/qmui_anim_duration\"/>\n\n</set>"
  },
  {
    "path": "arch/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <declare-styleable name=\"SwipeBackLayout\">\n        <attr name=\"shadow_left\" format=\"reference\"/>\n        <attr name=\"shadow_right\" format=\"reference\"/>\n        <attr name=\"shadow_bottom\" format=\"reference\"/>\n        <attr name=\"shadow_top\" format=\"reference\"/>\n    </declare-styleable>\n\n    <attr name=\"SwipeBackLayoutStyle\" format=\"reference\"/>\n</resources>"
  },
  {
    "path": "arch/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <item name=\"qmui_activity_root_id\" type=\"id\"/>\n    <item name=\"qmui_activity_fragment_container_id\" type=\"id\"/>\n    <item name=\"qmui_nav_fragment_container_id\" type=\"id\"/>\n\n    <item name=\"qmui_arch_swipe_layout_in_back\" type=\"id\"/>\n    <item name=\"qmui_arch_swipe_offset_helper\" type=\"id\"/>\n    <item name=\"qmui_arch_reused_layout\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "arch/src/main/res/values/qmui_integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!-- activity或fragment切换的动画时间 -->\n    <integer name=\"qmui_anim_duration\">300</integer>\n</resources>"
  },
  {
    "path": "arch/src/main/res/values/strings.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <string name=\"app_name\">arch</string>\n</resources>\n"
  },
  {
    "path": "arch/src/main/res/values/style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <style name=\"SwipeBackLayout\">\n        <item name=\"shadow_left\">@drawable/shadow_left</item>\n        <item name=\"shadow_right\">@drawable/shadow_right</item>\n        <item name=\"shadow_bottom\">@drawable/shadow_bottom</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "arch/src/test/java/com/qmuiteam/qmui/arch/ExampleUnitTest.java",
    "content": "package com.qmuiteam.qmui.arch;\n\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n\n}"
  },
  {
    "path": "arch-annotation/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "arch-annotation/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    `java-library`\n    kotlin(\"jvm\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.archVer\n\njava {\n    sourceCompatibility = Dep.javaVersion\n    targetCompatibility = Dep.javaVersion\n}"
  },
  {
    "path": "arch-annotation/src/main/java/com/qmuiteam/qmui/arch/annotation/ActivityScheme.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.CLASS)\n@Target(ElementType.TYPE)\npublic @interface ActivityScheme {\n    String name();\n    String[] required() default {};\n    boolean useRefreshIfCurrentMatched() default false;\n    Class<?> customMatcher() default void.class;\n    Class<?> customFactory() default void.class;\n    String[] keysWithIntValue() default {};\n    String[] keysWithBoolValue() default {};\n    String[] keysWithLongValue() default {};\n    String[] keysWithFloatValue() default {};\n    String[] keysWithDoubleValue() default {};\n    String[] defaultParams() default {};\n    Class<?> valueConverter() default void.class;\n}\n"
  },
  {
    "path": "arch-annotation/src/main/java/com/qmuiteam/qmui/arch/annotation/FragmentContainerParam.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n\n/**\n * used for activity for different business.\n *\n *example:\n *\n * FragmentContainerParam(required = {\"bookId\"})\n * class BookActivity extend QMUIFragmentActivity {\n *\n * }\n *\n * FragmentScheme(name = \"bookDetail\", activities = {BookActivity.class}, required={\"bookId\"})\n * class BookDetailFragment extend QMUIFragment {\n *\n * }\n *\n * FragmentScheme(name = \"bookRead\", activities = {BookActivity.class}, required={\"bookId\"})\n * class BookReadFragment extend QMUIFragment {\n *\n * }\n *\n * if bookId changed. QMUI will start up a new activity. so it's safe to put common book info\n * in activityViewModel.\n *\n *\n */\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface FragmentContainerParam {\n    String[] required() default {};\n    String[] any() default {};\n    String[] optional() default {};\n}\n"
  },
  {
    "path": "arch-annotation/src/main/java/com/qmuiteam/qmui/arch/annotation/FragmentScheme.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.CLASS)\n@Target(ElementType.TYPE)\npublic @interface FragmentScheme {\n    String name();\n    Class<?>[] activities();\n    String[] required() default {};\n    boolean useRefreshIfCurrentMatched() default false;\n    Class<?> customMatcher() default void.class;\n    boolean forceNewActivity() default false;\n    String forceNewActivityKey() default \"\";\n    Class<?> customFactory() default void.class;\n    String[] keysWithIntValue() default {};\n    String[] keysWithBoolValue() default {};\n    String[] keysWithLongValue() default {};\n    String[] keysWithFloatValue() default {};\n    String[] keysWithDoubleValue() default {};\n    String[] defaultParams() default {};\n    Class<?> valueConverter() default void.class;\n}\n"
  },
  {
    "path": "arch-annotation/src/main/java/com/qmuiteam/qmui/arch/annotation/LatestVisitRecord.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.arch.annotation;\n\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n\n/**\n * This annotation can be used when you want to revert to last Fragment(Activity) that\n * was visited before the app exited.\n * <p>\n * if annotated for subclass of QMUIFragment, such as FragmentA, it must be annotated\n * in the subclass of QMUIFragmentActivity, such as FragmentActivityA. FragmentActivityA\n * must be annotated by FirstFragments or DefaultFirstFragment and the value must contain\n * FragmentA.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface LatestVisitRecord {\n    boolean onlyForDebug() default false;\n}\n"
  },
  {
    "path": "arch-compiler/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "arch-compiler/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    `java-library`\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\nversion = Dep.QMUI.archVer\n\ndependencies {\n    implementation(project(\":arch-annotation\"))\n    implementation(Dep.CodeGen.javapoet)\n    implementation(Dep.CodeGen.autoService)\n    annotationProcessor(Dep.CodeGen.autoService)\n}\n\njava {\n    sourceCompatibility = Dep.javaVersion\n    targetCompatibility = Dep.javaVersion\n}"
  },
  {
    "path": "arch-compiler/src/main/java/com/qmuiteam/qmui/arch/BaseProcessor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch;\n\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.WildcardTypeName;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.processing.AbstractProcessor;\nimport javax.annotation.processing.Filer;\nimport javax.annotation.processing.Messager;\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.AnnotationMirror;\nimport javax.lang.model.element.AnnotationValue;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.ElementKind;\nimport javax.lang.model.element.ExecutableElement;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.DeclaredType;\nimport javax.lang.model.type.TypeKind;\nimport javax.lang.model.type.TypeMirror;\nimport javax.lang.model.util.Elements;\nimport javax.tools.Diagnostic;\n\nimport static javax.lang.model.element.ElementKind.INTERFACE;\n\npublic abstract class BaseProcessor extends AbstractProcessor {\n\n    static final String ACTIVITY_TYPE = \"android.app.Activity\";\n    static final String FRAGMENT_ACTIVITY_TYPE = \"androidx.fragment.app.FragmentActivity\";\n    static final String FRAGMENT_TYPE = \"androidx.fragment.app.Fragment\";\n    static final String QMUI_FRAGMENT_ACTIVITY_TYPE = \"com.qmuiteam.qmui.arch.QMUIFragmentActivity\";\n    static final String QMUI_FRAGMENT_TYPE = \"com.qmuiteam.qmui.arch.QMUIFragment\";\n    static final String QMUI_ACTIVITY_TYPE = \"com.qmuiteam.qmui.arch.QMUIActivity\";\n\n\n    static final ClassName QMUIFragmentActivityName = ClassName.get(\n            \"com.qmuiteam.qmui.arch\", \"QMUIFragmentActivity\");\n    static final ClassName QMUIFragmentName = ClassName.get(\n            \"com.qmuiteam.qmui.arch\", \"QMUIFragment\");\n    static ClassName MapName = ClassName.get(\"java.util\", \"Map\");\n    static ClassName ListName = ClassName.get(\"java.util\", \"List\");\n    static ClassName ArrayMapName = ClassName.get(\"android.util\", \"ArrayMap\");\n    static ClassName ArrayListName = ClassName.get(\"java.util\", \"ArrayList\");\n    static ClassName HashMapName = ClassName.get(\"java.util\", \"HashMap\");\n    static ClassName IntegerName = ClassName.get(\"java.lang\", \"Integer\");\n    static ClassName StringName = ClassName.get(\"java.lang\", \"String\");\n    static ClassName OriginClassName = ClassName.get(\"java.lang\", \"Class\");\n    static ParameterizedTypeName QMUIFragmentClassName = ParameterizedTypeName.get(\n            OriginClassName, WildcardTypeName.subtypeOf(QMUIFragmentName));\n\n    protected Filer mFiler;\n    protected Elements mElementUtils;\n    protected Messager mMessager;\n\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnvironment) {\n        super.init(processingEnvironment);\n        mFiler = processingEnv.getFiler();\n        mElementUtils = processingEnv.getElementUtils();\n        mMessager = processingEnv.getMessager();\n    }\n\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n\n    protected ExecutableElement getOverrideMethod(ClassName creator, String methodName) {\n        TypeElement element = mElementUtils.getTypeElement(creator.toString());\n        List<? extends Element> elements = element.getEnclosedElements();\n        for (Element ele : elements) {\n            if (ele.getKind() != ElementKind.METHOD) continue;\n            if (methodName.equals(ele.getSimpleName().toString())) {\n                return (ExecutableElement) ele;\n            }\n        }\n        throw new RuntimeException(String.format(\"method %s of interface FirstFragmentFinder not found\", methodName));\n    }\n\n\n    public void error(Element element, String message, Object... args) {\n        printMessage(Diagnostic.Kind.ERROR, element, message, args);\n    }\n\n    public void waring(Element element, String message, Object... args) {\n        printMessage(Diagnostic.Kind.WARNING, element, message, args);\n    }\n\n    public void note(Element element, String message, Object... args) {\n        printMessage(Diagnostic.Kind.NOTE, element, message, args);\n    }\n\n    private void printMessage(Diagnostic.Kind kind, Element element, String message, Object[] args) {\n        if (args.length > 0) {\n            message = String.format(message, args);\n        }\n\n        mMessager.printMessage(kind, message, element);\n    }\n\n    static boolean isSubtypeOfType(TypeMirror typeMirror, String otherType) {\n        if (isTypeEqual(typeMirror, otherType)) {\n            return true;\n        }\n        if (typeMirror.getKind() != TypeKind.DECLARED) {\n            return false;\n        }\n        DeclaredType declaredType = (DeclaredType) typeMirror;\n        List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();\n        if (typeArguments.size() > 0) {\n            StringBuilder typeString = new StringBuilder(declaredType.asElement().toString());\n            typeString.append('<');\n            for (int i = 0; i < typeArguments.size(); i++) {\n                if (i > 0) {\n                    typeString.append(',');\n                }\n                typeString.append('?');\n            }\n            typeString.append('>');\n            if (typeString.toString().equals(otherType)) {\n                return true;\n            }\n        }\n        Element element = declaredType.asElement();\n        if (!(element instanceof TypeElement)) {\n            return false;\n        }\n        TypeElement typeElement = (TypeElement) element;\n        TypeMirror superType = typeElement.getSuperclass();\n        if (isSubtypeOfType(superType, otherType)) {\n            return true;\n        }\n        for (TypeMirror interfaceType : typeElement.getInterfaces()) {\n            if (isSubtypeOfType(interfaceType, otherType)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    static boolean isTypeEqual(TypeMirror typeMirror, String otherType) {\n        return otherType.equals(typeMirror.toString());\n    }\n\n    static boolean isInterface(TypeMirror typeMirror) {\n        return typeMirror instanceof DeclaredType\n                && ((DeclaredType) typeMirror).asElement().getKind() == INTERFACE;\n    }\n\n    static AnnotationMirror getAnnotationMirror(Element element, Class<?> annotation) {\n        List<? extends AnnotationMirror> list = element.getAnnotationMirrors();\n        if (list == null || list.isEmpty()) {\n            return null;\n        }\n        for (AnnotationMirror item : list) {\n            if (item.getAnnotationType().toString().equals(annotation.getName())) {\n                return item;\n            }\n        }\n        return null;\n    }\n\n    static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String key) {\n        Map<? extends ExecutableElement, ? extends AnnotationValue> map = annotationMirror.getElementValues();\n        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> item : map.entrySet()) {\n            if (item.getKey().getSimpleName().toString().equals(key)) {\n                return item.getValue();\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "arch-compiler/src/main/java/com/qmuiteam/qmui/arch/LatestVisitProcessor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch;\n\nimport com.google.auto.service.AutoService;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.FieldSpec;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.TypeName;\nimport com.squareup.javapoet.TypeSpec;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.ExecutableElement;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.TypeMirror;\n\n@AutoService(Processor.class)\npublic class LatestVisitProcessor extends BaseProcessor {\n\n    private static ClassName RecordIdClassMap = ClassName.get(\n            \"com.qmuiteam.qmui.arch.record\", \"RecordIdClassMap\");\n\n    private static TypeName MapByClassName = ParameterizedTypeName.get(MapName,\n            OriginClassName, IntegerName);\n    private static TypeName MapByIdName = ParameterizedTypeName.get(MapName,\n            IntegerName, OriginClassName);\n\n    @Override\n    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(LatestVisitRecord.class);\n        if (elements.isEmpty()) {\n            return true;\n        }\n        TypeSpec.Builder classBuilder = TypeSpec\n                .classBuilder(RecordIdClassMap.simpleName() + \"Impl\")\n                .addModifiers(Modifier.PUBLIC)\n                .addSuperinterface(RecordIdClassMap);\n        classBuilder.addField(FieldSpec.builder(MapByClassName, \"mClassToIdMap\")\n                .addModifiers(Modifier.PRIVATE)\n                .build());\n\n        classBuilder.addField(FieldSpec.builder(MapByIdName, \"mIdToClassMap\")\n                .addModifiers(Modifier.PRIVATE)\n                .build());\n\n        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()\n                .addModifiers(Modifier.PUBLIC)\n                .addStatement(\"mClassToIdMap = new $T<>()\", HashMapName)\n                .addStatement(\"mIdToClassMap = new $T<>()\", HashMapName);\n\n\n        HashMap<Integer, String> hashCodes = new HashMap<>();\n        for (Element element : elements) {\n            if (element instanceof TypeElement) {\n                TypeElement classElement = (TypeElement) element;\n                TypeMirror elementType = classElement.asType();\n                boolean isFragmentActivity = isSubtypeOfType(elementType, QMUI_FRAGMENT_ACTIVITY_TYPE);\n                boolean isFragment = isSubtypeOfType(elementType, QMUI_FRAGMENT_TYPE);\n                boolean isActivity = isSubtypeOfType(elementType, QMUI_ACTIVITY_TYPE);\n                if (isFragmentActivity || isFragment || isActivity) {\n                    ClassName elementName = ClassName.get(classElement);\n                    String simpleName = elementName.simpleName();\n                    int hashCode = simpleName.hashCode();\n                    if(hashCodes.keySet().contains(hashCode)){\n                        if(hashCodes.keySet().contains(hashCode)){\n                            error(element, \"The hashCode of \" + simpleName + \" conflict with \"\n                                    + hashCodes.get(hashCode) + \"; Please consider changing the class name\");\n                            continue;\n                        }\n                    }\n                    hashCodes.put(hashCode, simpleName);\n\n                    constructorBuilder.addStatement(\"mClassToIdMap.put($T.class, $L)\",\n                            elementName,\n                            hashCode);\n                    constructorBuilder.addStatement(\"mIdToClassMap.put($L, $T.class)\",\n                            hashCode,\n                            elementName);\n                } else {\n                    error(element, \"Must annotated on subclasses of QMUIFragmentActivity\");\n                }\n            }\n        }\n\n        ExecutableElement iGetClassById = getOverrideMethod(\n                RecordIdClassMap, \"getRecordClassById\");\n        MethodSpec.Builder getRecordMetaById = MethodSpec.overriding(iGetClassById)\n                .addStatement(\"return mIdToClassMap.get($L)\",\n                        iGetClassById.getParameters().get(0).getSimpleName().toString());\n        ExecutableElement iGetIdByClass = getOverrideMethod(\n                RecordIdClassMap, \"getIdByRecordClass\");\n        MethodSpec.Builder getRecordMetaByClass = MethodSpec.overriding(iGetIdByClass)\n                .addStatement(\"return mClassToIdMap.get($L)\",\n                        iGetIdByClass.getParameters().get(0).getSimpleName().toString());\n\n        classBuilder\n                .addMethod(constructorBuilder.build())\n                .addMethod(getRecordMetaById.build())\n                .addMethod(getRecordMetaByClass.build());\n        try {\n            JavaFile.builder(RecordIdClassMap.packageName(), classBuilder.build())\n                    .build().writeTo(mFiler);\n        } catch (IOException e) {\n            error(null, \"Unable to generate RecordMetaMapImpl: %s\", e.getMessage());\n        }\n        return true;\n    }\n\n    @Override\n    public Set<String> getSupportedAnnotationTypes() {\n        Set<String> types = new LinkedHashSet<>();\n        types.add(LatestVisitRecord.class.getCanonicalName());\n        return types;\n    }\n}\n"
  },
  {
    "path": "arch-compiler/src/main/java/com/qmuiteam/qmui/arch/SchemeProcessor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.arch;\n\nimport com.google.auto.service.AutoService;\nimport com.qmuiteam.qmui.arch.annotation.ActivityScheme;\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.CodeBlock;\nimport com.squareup.javapoet.FieldSpec;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.TypeName;\nimport com.squareup.javapoet.TypeSpec;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.lang.model.element.AnnotationMirror;\nimport javax.lang.model.element.AnnotationValue;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.ExecutableElement;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.element.VariableElement;\nimport javax.lang.model.type.MirroredTypesException;\nimport javax.lang.model.type.TypeMirror;\n\n@AutoService(Processor.class)\npublic class SchemeProcessor extends BaseProcessor {\n    private static String QMUISchemeIntentFactoryType = \"com.qmuiteam.qmui.arch.scheme.QMUISchemeIntentFactory\";\n    private static String QMUISchemeFragmentFactoryType = \"com.qmuiteam.qmui.arch.scheme.QMUISchemeFragmentFactory\";\n    private static String QMUISchemeMatcherType = \"com.qmuiteam.qmui.arch.scheme.QMUISchemeMatcher\";\n    private static String QMUISchemeValueConverterType = \"com.qmuiteam.qmui.arch.scheme.QMUISchemeValueConverter\";\n\n    private static ClassName SchemeMap = ClassName.get(\n            \"com.qmuiteam.qmui.arch.scheme\", \"SchemeMap\");\n    private static ClassName SchemeItem = ClassName.get(\n            \"com.qmuiteam.qmui.arch.scheme\", \"SchemeItem\");\n    private static ClassName ActivitySchemeItem = ClassName.get(\n            \"com.qmuiteam.qmui.arch.scheme\", \"ActivitySchemeItem\");\n    private static ClassName FragmentSchemeItem = ClassName.get(\n            \"com.qmuiteam.qmui.arch.scheme\", \"FragmentSchemeItem\");\n\n    private static TypeName SchemeItemList = ParameterizedTypeName.get(ListName, SchemeItem);\n    private static TypeName MapByAction = ParameterizedTypeName.get(MapName,\n            StringName, SchemeItemList);\n    private static TypeName MapForSchemeRequired = ParameterizedTypeName.get(ArrayMapName,\n            StringName, StringName);\n\n    @Override\n    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n        Set<? extends Element> activitySchemes = roundEnv.getElementsAnnotatedWith(ActivityScheme.class);\n        Set<? extends Element> fragmentSchemes = roundEnv.getElementsAnnotatedWith(FragmentScheme.class);\n        if (activitySchemes.isEmpty() && fragmentSchemes.isEmpty()) {\n            return true;\n        }\n        TypeSpec.Builder classBuilder = TypeSpec\n                .classBuilder(SchemeMap.simpleName() + \"Impl\")\n                .addModifiers(Modifier.PUBLIC)\n                .addSuperinterface(SchemeMap);\n        classBuilder.addField(FieldSpec.builder(MapByAction, \"mSchemeMap\")\n                .addModifiers(Modifier.PRIVATE)\n                .build());\n\n\n        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()\n                .addModifiers(Modifier.PUBLIC)\n                .addStatement(\"mSchemeMap = new $T<>()\", HashMapName);\n\n\n        Map<String, List<Item>> schemeMap = new HashMap<>();\n        for (Element element : activitySchemes) {\n            if (element instanceof TypeElement) {\n                TypeElement classElement = (TypeElement) element;\n                TypeMirror elementType = classElement.asType();\n                boolean isActivity = isSubtypeOfType(elementType, ACTIVITY_TYPE);\n                if (!isActivity) {\n                    error(element, \"Must annotated on subclasses of Activity\");\n                } else {\n                    ActivityScheme annotation = element.getAnnotation(ActivityScheme.class);\n                    String name = annotation.name();\n                    List<Item> elements = schemeMap.get(name);\n                    if (elements == null) {\n                        elements = new ArrayList<>();\n                        schemeMap.put(name, elements);\n                    }\n                    elements.add(new Item(true, classElement, elementType, annotation.required()));\n\n                }\n            }\n        }\n\n        for (Element element : fragmentSchemes) {\n            if (element instanceof TypeElement) {\n                TypeElement classElement = (TypeElement) element;\n                TypeMirror elementType = classElement.asType();\n                boolean isQMUIFragment = isSubtypeOfType(elementType, QMUI_FRAGMENT_TYPE);\n                if (!isQMUIFragment) {\n                    error(element, \"Must annotated on subclasses of QMUIFragment\");\n                } else {\n                    FragmentScheme annotation = element.getAnnotation(FragmentScheme.class);\n                    String name = annotation.name();\n                    List<Item> elements = schemeMap.get(name);\n                    if (elements == null) {\n                        elements = new ArrayList<>();\n                        schemeMap.put(name, elements);\n                    }\n                    elements.add(new Item(false, classElement, elementType, annotation.required()));\n\n                }\n            }\n        }\n\n        constructorBuilder.addStatement(\"$T elements\", SchemeItemList);\n        constructorBuilder.addStatement(\"$T required = null\", MapForSchemeRequired);\n        for (String key : schemeMap.keySet()) {\n            List<Item> items = schemeMap.get(key);\n            constructorBuilder.addStatement(\"elements = new $T<>()\", ArrayListName);\n            items.sort(new Comparator<Item>() {\n                @Override\n                public int compare(Item item, Item t1) {\n                    int c1 = item.getRequiredCount();\n                    int c2 = t1.getRequiredCount();\n                    return Integer.compare(c2, c1);\n                }\n            });\n            for (Item item : items) {\n                ClassName elementName = ClassName.get(item.element);\n                if (item.isActivity) {\n                    ActivityScheme annotation = item.element.getAnnotation(ActivityScheme.class);\n                    AnnotationMirror annotationMirror = getAnnotationMirror(item.element, ActivityScheme.class);\n                    if (annotationMirror == null) {\n                        continue;\n                    }\n\n                    appendRequired(constructorBuilder, annotation.required());\n\n                    CodeBlock customFactory = generateCustomFactory(true, annotationMirror);\n                    CodeBlock intParam = generateTypedParams(annotation.keysWithIntValue());\n                    CodeBlock boolParam = generateTypedParams(annotation.keysWithBoolValue());\n                    CodeBlock longParam = generateTypedParams(annotation.keysWithLongValue());\n                    CodeBlock floatParam = generateTypedParams(annotation.keysWithFloatValue());\n                    CodeBlock doubleParam = generateTypedParams(annotation.keysWithDoubleValue());\n                    CodeBlock defaultParam = generateTypedParams(annotation.defaultParams());\n                    CodeBlock customMatcher = generateCustomMatcher(annotationMirror);\n                    CodeBlock valueConverter = generateValueInterceptor(annotationMirror);\n\n                    CodeBlock codeBlock = CodeBlock.builder()\n                            .add(\"elements.add(\")\n                            /**/.add(\"new $T(\", ActivitySchemeItem)\n                            /*---*/.add(\"$T.class\", elementName)\n                            /*---*/.add(\",\")\n                            /*---*/.add(\"$L\", annotation.useRefreshIfCurrentMatched())\n                            /*---*/.add(\",\")\n                            /*---*/.add(customFactory)\n                            /*---*/.add(\",\")\n                            /*---*/.add(\"required\")\n                            /*---*/.add(\",\")\n                            /*---*/.add(intParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(boolParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(longParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(floatParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(doubleParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(defaultParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(customMatcher)\n                            /*---*/.add(\",\")\n                            /*---*/.add(valueConverter)\n                            /**/.add(\")\")\n                            .add(\")\")\n                            .build();\n                    constructorBuilder.addStatement(codeBlock);\n                } else {\n                    FragmentScheme annotation = item.element.getAnnotation(FragmentScheme.class);\n                    AnnotationMirror annotationMirror = getAnnotationMirror(item.element, FragmentScheme.class);\n                    if (annotationMirror == null) {\n                        continue;\n                    }\n                    appendRequired(constructorBuilder, annotation.required());\n                    CodeBlock customFactory = generateCustomFactory(false, annotationMirror);\n                    CodeBlock activities = generateFragmentHostActivityList(annotation);\n                    CodeBlock intParam = generateTypedParams(annotation.keysWithIntValue());\n                    CodeBlock boolParam = generateTypedParams(annotation.keysWithBoolValue());\n                    CodeBlock longParam = generateTypedParams(annotation.keysWithLongValue());\n                    CodeBlock floatParam = generateTypedParams(annotation.keysWithFloatValue());\n                    CodeBlock doubleParam = generateTypedParams(annotation.keysWithDoubleValue());\n                    CodeBlock defaultParam = generateTypedParams(annotation.defaultParams());\n                    CodeBlock customMatcher = generateCustomMatcher(annotationMirror);\n                    CodeBlock valueConverter = generateValueInterceptor(annotationMirror);\n\n                    CodeBlock codeBlock = CodeBlock.builder()\n                            .add(\"elements.add(\")\n                            /**/.add(\"new $T(\", FragmentSchemeItem)\n                            /*---*/.add(\"$T.class\", elementName)\n                            /*---*/.add(\",\")\n                            /*---*/.add(\"$L\", annotation.useRefreshIfCurrentMatched())\n                            /*---*/.add(\",\")\n                            /*---*/.add(activities)\n                            /*---*/.add(\",\")\n                            /*---*/.add(customFactory)\n                            /*---*/.add(\",\")\n                            /*---*/.add(\"$L\", annotation.forceNewActivity())\n                            /*---*/.add(\",\")\n                            /*---*/.add(\"required\")\n                            /*---*/.add(\",\")\n                            /*---*/.add(intParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(boolParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(longParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(floatParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(doubleParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(defaultParam)\n                            /*---*/.add(\",\")\n                            /*---*/.add(customMatcher)\n                            /*---*/.add(\",\")\n                            /*---*/.add(valueConverter)\n                            /**/.add(\")\")\n                            .add(\")\")\n                            .build();\n                    constructorBuilder.addStatement(codeBlock);\n                }\n            }\n\n            constructorBuilder.addStatement(\"mSchemeMap.put($S, elements)\", key);\n        }\n\n        ExecutableElement findScheme = getOverrideMethod(\n                SchemeMap, \"findScheme\");\n        List<? extends VariableElement> findSchemeParams = findScheme.getParameters();\n        String schemeHandler = findSchemeParams.get(0).getSimpleName().toString();\n        String schemeAction = findSchemeParams.get(1).getSimpleName().toString();\n        String schemeParam = findSchemeParams.get(2).getSimpleName().toString();\n        MethodSpec.Builder getRecordMetaById = MethodSpec.overriding(findScheme)\n                .addStatement(\"$T list = mSchemeMap.get($L)\", SchemeItemList, schemeAction)\n                .beginControlFlow(\"if(list == null || list.isEmpty())\")\n                /**/.addStatement(\"return null\")\n                .endControlFlow()\n                .beginControlFlow(\"for (int i = 0; i < list.size(); i++)\")\n                /**/.addStatement(\"$T item = list.get(i)\", SchemeItem)\n                /**/.beginControlFlow(\"if(item.match($L, $L))\", schemeHandler, schemeParam)\n                /*--*/.addStatement(\"return item\")\n                /**/.endControlFlow()\n                .endControlFlow()\n                .addStatement(\"return null\");\n        ExecutableElement exists = getOverrideMethod(\n                SchemeMap, \"exists\");\n        MethodSpec.Builder getRecordMetaByClass = MethodSpec.overriding(exists)\n                .addStatement(\"return mSchemeMap.containsKey($L)\", exists.getParameters().get(1).getSimpleName().toString());\n\n        classBuilder\n                .addMethod(constructorBuilder.build())\n                .addMethod(getRecordMetaById.build())\n                .addMethod(getRecordMetaByClass.build());\n        try {\n            JavaFile.builder(SchemeMap.packageName(), classBuilder.build())\n                    .build().writeTo(mFiler);\n        } catch (IOException e) {\n            error(null, \"Unable to generate RecordMetaMapImpl: %s\", e.getMessage());\n        }\n        return true;\n    }\n\n    private void appendRequired(MethodSpec.Builder constructorBuilder, String[] required) {\n        if (required == null || required.length == 0) {\n            constructorBuilder.addStatement(\"required =null\");\n            return;\n        }\n        constructorBuilder.addStatement(\"required = new $T<>()\", ArrayMapName);\n        for (int i = 0; i < required.length; i++) {\n            String condition = required[i];\n            if (condition == null || condition.isEmpty()) {\n                continue;\n            }\n            int index = condition.indexOf(\"=\");\n            if (index < 0 || index >= condition.length()) {\n                constructorBuilder.addStatement(\"required.put($S, null)\", condition);\n            } else {\n                String key = condition.substring(0, index);\n                String value = index == condition.length() - 1 ? \"\" : condition.substring(index + 1);\n                constructorBuilder.addStatement(\"required.put($S, $S)\", key, value);\n            }\n        }\n    }\n\n    private CodeBlock generateTypedParams(String[] keys) {\n        CodeBlock.Builder builder = CodeBlock.builder();\n        if (keys == null || keys.length == 0) {\n            builder.add(\"null\");\n        } else {\n            builder.add(\"new $T[]{\", StringName);\n            for (int i = 0; i < keys.length; i++) {\n                if (i != 0) {\n                    builder.add(\",\");\n                }\n                builder.add(\"$S\", keys[i]);\n            }\n            builder.add(\"}\");\n        }\n        return builder.build();\n    }\n\n\n    private CodeBlock generateCustomFactory(boolean isActivity, AnnotationMirror annotationMirror){\n        AnnotationValue customFactory = getAnnotationValue(annotationMirror, \"customFactory\");\n        if (customFactory == null) {\n            return CodeBlock.of(\"null\");\n        }\n        TypeMirror typeMirror = (TypeMirror) customFactory.getValue();\n        if(isActivity){\n            if (!isSubtypeOfType(typeMirror, QMUISchemeIntentFactoryType)) {\n                throw new IllegalStateException(\"customFactory must implement interface QMUISchemeIntentFactory.\");\n            }\n        }else{\n            if (!isSubtypeOfType(typeMirror, QMUISchemeFragmentFactoryType)) {\n                throw new IllegalStateException(\"customFactory must implement interface QMUISchemeFragmentFactory.\");\n            }\n        }\n\n        return CodeBlock.of(\"$T.class\", typeMirror);\n    }\n\n    private CodeBlock generateCustomMatcher(AnnotationMirror annotationMirror){\n        AnnotationValue customFactory = getAnnotationValue(annotationMirror, \"customMatcher\");\n        if (customFactory == null) {\n            return CodeBlock.of(\"null\");\n        }\n        TypeMirror typeMirror = (TypeMirror) customFactory.getValue();\n        if (!isSubtypeOfType(typeMirror, QMUISchemeMatcherType)) {\n            throw new IllegalStateException(\"customMatcher must implement interface QMUISchemeMatcher.\");\n        }\n\n        return CodeBlock.of(\"$T.class\", typeMirror);\n    }\n\n    private CodeBlock generateValueInterceptor(AnnotationMirror annotationMirror){\n        AnnotationValue valueConverter = getAnnotationValue(annotationMirror, \"valueConverter\");\n        if (valueConverter == null) {\n            return CodeBlock.of(\"null\");\n        }\n        TypeMirror typeMirror = (TypeMirror) valueConverter.getValue();\n        if (!isSubtypeOfType(typeMirror, QMUISchemeValueConverterType)) {\n            throw new IllegalStateException(\"customMatcher must implement interface QMUISchemeMatcher.\");\n        }\n\n        return CodeBlock.of(\"$T.class\", typeMirror);\n    }\n\n    private CodeBlock generateFragmentHostActivityList(FragmentScheme fragmentScheme){\n        CodeBlock.Builder builder = CodeBlock.builder();\n        TypeMirror[] activities = null;\n        try {\n            fragmentScheme.activities();\n        } catch (MirroredTypesException mte) {\n            List<? extends TypeMirror> containerMirrors = mte.getTypeMirrors();\n            activities = new TypeMirror[containerMirrors.size()];\n            for (int i = 0; i < activities.length; i++) {\n                activities[i] = containerMirrors.get(i);\n            }\n        }\n        if(activities == null || activities.length == 0){\n            throw new IllegalStateException(\"FragmentScheme#activities can not be empty.\");\n        }\n        builder.add(\"new $T[]{\", OriginClassName);\n        for(int i=0; i < activities.length; i++){\n            TypeMirror item = activities[i];\n            if(!isSubtypeOfType(item, QMUI_FRAGMENT_ACTIVITY_TYPE)){\n                throw new IllegalStateException(\"FragmentScheme#activities must be QMUIFragmentActivity.\");\n            }\n            if(i > 0){\n                builder.add(\",\");\n            }\n            builder.add(\"$T.class\", ClassName.get(item));\n        }\n        builder.add(\"}\");\n        return builder.build();\n    }\n\n    @Override\n    public Set<String> getSupportedAnnotationTypes() {\n        Set<String> types = new LinkedHashSet<>();\n        types.add(ActivityScheme.class.getCanonicalName());\n        types.add(FragmentScheme.class.getCanonicalName());\n        return types;\n    }\n\n    static class Item {\n        boolean isActivity = false;\n        TypeElement element;\n        TypeMirror type;\n        String[] required;\n\n        public Item(boolean isActivity, TypeElement element, TypeMirror type, String[] required) {\n            this.isActivity = isActivity;\n            this.element = element;\n            this.type = type;\n            this.required = required;\n        }\n\n        int getRequiredCount(){\n            return required == null ? 0 : required.length;\n        }\n    }\n}\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nimport com.qmuiteam.plugin.Dep\nbuildscript {\n    repositories {\n        mavenCentral()\n        google()\n        mavenLocal()\n    }\n    dependencies {\n        classpath(\"com.android.tools.build:gradle:7.2.1\")\n        classpath(\"org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20\")\n    }\n\n}\n\nplugins {\n    id(\"qmui-dep\")\n    id(\"com.osacky.doctor\") version \"0.8.0\"\n}\n\nsubprojects {\n    group = Dep.QMUI.group\n}\n\nallprojects {\n    repositories {\n        mavenCentral()\n        google()\n        mavenLocal()\n    }\n}\n"
  },
  {
    "path": "compiler/.gitignore",
    "content": "/build\n/*.iml"
  },
  {
    "path": "compiler/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    `java-library`\n}\n\njava {\n    sourceCompatibility = Dep.javaVersion\n    targetCompatibility = Dep.javaVersion\n}\ndependencies {\n    implementation(project(\":lib\"))\n    implementation(Dep.CodeGen.javapoet)\n    implementation(Dep.CodeGen.autoService)\n    annotationProcessor(Dep.CodeGen.autoService)\n}\n"
  },
  {
    "path": "compiler/src/main/java/com/qmuiteam/qmuidemo/compiler/WidgetProcessor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.compiler;\n\nimport com.google.auto.service.AutoService;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.FieldSpec;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.TypeName;\nimport com.squareup.javapoet.TypeSpec;\nimport com.squareup.javapoet.WildcardTypeName;\n\nimport java.io.IOException;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport javax.annotation.processing.AbstractProcessor;\nimport javax.annotation.processing.Filer;\nimport javax.annotation.processing.Messager;\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.DeclaredType;\nimport javax.lang.model.type.MirroredTypeException;\nimport javax.lang.model.type.TypeKind;\nimport javax.lang.model.type.TypeMirror;\nimport javax.lang.model.util.Elements;\nimport javax.tools.Diagnostic;\n\n/**\n * @author cginechen\n * @date 2016-12-13\n */\n\n@AutoService(Processor.class)\npublic class WidgetProcessor extends AbstractProcessor {\n    private Filer mFiler; //文件相关的辅助类\n    @SuppressWarnings(\"FieldCanBeLocal\") private Elements mElementUtils; //元素相关的辅助类\n    private Messager mMessager; //日志相关的辅助类\n    private boolean mIsFileCreated = false;\n\n    private final String mClassName = \"QDWidgetContainer\";\n    private final String mPackageName = \"com.qmuiteam.qmuidemo.manager\";\n\n    ClassName mMapName = ClassName.get(\"java.util\", \"Map\");\n    ClassName mHashMapName = ClassName.get(\"java.util\", \"HashMap\");\n    ClassName mItemDescName = ClassName.get(\"com.qmuiteam.qmuidemo.model\", \"QDItemDescription\");\n    ClassName mBaseFragmentName = ClassName.get(\"com.qmuiteam.qmuidemo.base\", \"BaseFragment\");\n    TypeName mBaseFragmentClassName = ParameterizedTypeName.get(ClassName.get(Class.class),\n            WildcardTypeName.subtypeOf(mBaseFragmentName));\n    TypeName mMapFieldTypeName = ParameterizedTypeName.get(mMapName,\n            mBaseFragmentClassName, mItemDescName);\n\n    ClassName mWidgetContainerName = ClassName.get(mPackageName, mClassName);\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnv) {\n        super.init(processingEnv);\n        mFiler = processingEnv.getFiler();\n        mElementUtils = processingEnv.getElementUtils();\n        mMessager = processingEnv.getMessager();\n        mIsFileCreated = false;\n    }\n\n    /**\n     * @return 指定哪些注解应该被注解处理器注册\n     */\n    @Override\n    public Set<String> getSupportedAnnotationTypes() {\n        Set<String> types = new LinkedHashSet<>();\n        types.add(Widget.class.getCanonicalName());\n        return types;\n    }\n\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n    @Override\n    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {\n        if (mIsFileCreated) {\n            return true;\n        }\n        mIsFileCreated = true;\n        TypeSpec.Builder widgetContainerBuilder = TypeSpec.classBuilder(mWidgetContainerName);\n\n        FieldSpec instanceField = FieldSpec.builder(mWidgetContainerName, \"sInstance\")\n                .addModifiers(Modifier.PRIVATE)\n                .addModifiers(Modifier.STATIC)\n                .initializer(\"new $T()\", mWidgetContainerName)\n                .build();\n\n        FieldSpec mapField = FieldSpec.builder(mMapFieldTypeName, \"mWidgets\")\n                .addModifiers(Modifier.PRIVATE)\n                .build();\n\n        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()\n                .addModifiers(Modifier.PRIVATE)\n                .addStatement(\"mWidgets = new $T<>()\", mHashMapName);\n\n        for (Element element : roundEnvironment.getElementsAnnotatedWith(Widget.class)) {\n            if (element instanceof TypeElement) {\n                TypeElement classElement = (TypeElement) element;\n                ClassName elementName = ClassName.get(classElement);\n                Widget widget = classElement.getAnnotation(Widget.class);\n                String name = null;\n                // http://www.programcreek.com/java-api-examples/index.php?api=javax.lang.model.type.TypeMirror\n                // https://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/\n                try {\n                    widget.widgetClass();\n                } catch (MirroredTypeException mte) {\n                    TypeMirror nameMirror = mte.getTypeMirror();\n                    if (nameMirror.getKind() == TypeKind.DECLARED) {\n                        name = ((DeclaredType) nameMirror).asElement().getSimpleName().toString();\n                    }\n                }\n                if (name == null && widget.name().length() > 0) {\n                    name = widget.name();\n                }\n\n                if (name == null || name.length() == 0) {\n                    error(\"please provide widgetClass or name\");\n                }\n                constructorBuilder.addStatement(\"mWidgets.put($T.class, new $T($T.class, $S, $L, $S))\",\n                        elementName,\n                        mItemDescName,\n                        elementName,\n                        name,\n                        widget.iconRes(),\n                        widget.docUrl());\n            }\n\n        }\n        MethodSpec constructorMethod = constructorBuilder.build();\n\n        MethodSpec instanceMethod = MethodSpec.methodBuilder(\"getInstance\")\n                .addModifiers(Modifier.PUBLIC)\n                .addModifiers(Modifier.STATIC)\n                .returns(mWidgetContainerName)\n                .addStatement(\"return sInstance\")\n                .build();\n\n        MethodSpec getMethod = MethodSpec.methodBuilder(\"get\")\n                .addModifiers(Modifier.PUBLIC)\n                .returns(mItemDescName)\n                .addParameter(mBaseFragmentClassName, \"fragment\")\n                .addStatement(\"return mWidgets.get($L)\", \"fragment\")\n                .build();\n\n        try {\n            widgetContainerBuilder\n                    .addField(instanceField)\n                    .addField(mapField)\n                    .addMethod(constructorMethod)\n                    .addMethod(instanceMethod)\n                    .addMethod(getMethod);\n            JavaFile.builder(mPackageName, widgetContainerBuilder.build()).build().writeTo(mFiler);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return true;\n    }\n\n    private void error(String msg, Object... args) {\n        mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));\n    }\n\n    private void info(String msg, Object... args) {\n        mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));\n    }\n}\n"
  },
  {
    "path": "compose/.gitignore",
    "content": "/build"
  },
  {
    "path": "compose/build.gradle.kts",
    "content": "\nimport com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.composeVer\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    buildFeatures {\n        compose = true\n    }\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n}\n\ndependencies {\n    api(project(\":compose-core\"))\n    api(Dep.Compose.constraintlayout)\n}"
  },
  {
    "path": "compose/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "compose/src/androidTest/java/com/qmuiteam/compose/ExampleInstrumentedTest.kt",
    "content": "package com.qmuiteam.compose\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.qmuiteam.compose\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "compose/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qmuiteam.compose\">\n\n</manifest>"
  },
  {
    "path": "compose/src/main/java/com/qmuiteam/compose/modal/ModalImpl.kt",
    "content": "package com.qmuiteam.compose.modal\n\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport androidx.activity.OnBackPressedCallback\nimport androidx.activity.OnBackPressedDispatcher\nimport androidx.compose.animation.*\nimport androidx.compose.animation.core.tween\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.ComposeView\nimport kotlinx.coroutines.flow.MutableStateFlow\n\ninternal abstract class QMUIModalPresent(\n    private val rootLayout: FrameLayout,\n    private val onBackPressedDispatcher: OnBackPressedDispatcher,\n    val mask: Color = DefaultMaskColor,\n    val systemCancellable: Boolean = true,\n    val maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n) : QMUIModal {\n\n    private val onShowListeners = arrayListOf<QMUIModal.Action>()\n    private val onDismissListeners = arrayListOf<QMUIModal.Action>()\n    private val visibleFlow = MutableStateFlow(false)\n    private var isShown = false\n    private var isDismissing = false\n\n    private val composeLayout = ComposeView(rootLayout.context).apply {\n        visibility = View.GONE\n    }\n\n    private val onBackPressedCallback = object : OnBackPressedCallback(systemCancellable) {\n        override fun handleOnBackPressed() {\n            dismiss()\n        }\n    }\n\n    init {\n        composeLayout.setContent {\n            Box(modifier = Modifier.fillMaxSize()) {\n                val visible by visibleFlow.collectAsState(initial = false)\n                ModalContent(visible = visible) {\n                    if (isDismissing) {\n                        doAfterDismiss()\n                    }\n                }\n            }\n        }\n    }\n\n    private fun doAfterDismiss() {\n        isDismissing = false\n        composeLayout.visibility = View.GONE\n        composeLayout.disposeComposition()\n        rootLayout.removeView(composeLayout)\n        onBackPressedCallback.remove()\n        onDismissListeners.forEach {\n            it.invoke(this)\n        }\n    }\n\n    @Composable\n    abstract fun ModalContent(visible: Boolean, dismissFinishAction: () -> Unit)\n\n    override fun isShowing(): Boolean {\n        return isShown\n    }\n\n    override fun show(): QMUIModal {\n        if (isShown || isDismissing) {\n            return this\n        }\n        isShown = true\n        rootLayout.addView(composeLayout, generateLayoutParams())\n        composeLayout.visibility = View.VISIBLE\n        visibleFlow.value = true\n        onBackPressedDispatcher.addCallback(onBackPressedCallback)\n        onShowListeners.forEach {\n            it.invoke(this)\n        }\n        return this\n    }\n\n    open fun generateLayoutParams(): FrameLayout.LayoutParams {\n        return FrameLayout.LayoutParams(\n            ViewGroup.LayoutParams.MATCH_PARENT,\n            ViewGroup.LayoutParams.MATCH_PARENT\n        )\n    }\n\n    override fun dismiss() {\n        if (!isShown) {\n            return\n        }\n        isShown = false\n        isDismissing = true\n        visibleFlow.value = false\n    }\n\n    override fun doOnShow(listener: QMUIModal.Action): QMUIModal {\n        onShowListeners.add(listener)\n        return this\n    }\n\n    override fun doOnDismiss(listener: QMUIModal.Action): QMUIModal {\n        onDismissListeners.add(listener)\n        return this\n    }\n\n    override fun removeOnShowAction(listener: QMUIModal.Action): QMUIModal {\n        onShowListeners.remove(listener)\n        return this\n    }\n\n    override fun removeOnDismissAction(listener: QMUIModal.Action): QMUIModal {\n        onDismissListeners.remove(listener)\n        return this\n    }\n}\n\ninternal class StillModalImpl(\n    rootLayout: FrameLayout,\n    onBackPressedDispatcher: OnBackPressedDispatcher,\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    val content: @Composable (modal: QMUIModal) -> Unit\n) : QMUIModalPresent(rootLayout, onBackPressedDispatcher, mask, systemCancellable, maskTouchBehavior) {\n\n    @Composable\n    override fun ModalContent(visible: Boolean, dismissFinishAction: () -> Unit) {\n        if (visible) {\n            Box(\n                modifier = Modifier\n                    .fillMaxSize()\n                    .background(mask)\n                    .let {\n                        if (maskTouchBehavior == MaskTouchBehavior.penetrate) {\n                            it\n                        } else {\n                            it.clickable(\n                                interactionSource = remember { MutableInteractionSource() },\n                                indication = null,\n                                enabled = maskTouchBehavior == MaskTouchBehavior.dismiss\n                            ) {\n                                dismiss()\n                            }\n                        }\n                    }\n            )\n            content(this)\n        } else {\n            DisposableEffect(\"\") {\n                onDispose {\n                    dismissFinishAction()\n                }\n            }\n        }\n    }\n}\n\ninternal class AnimateModalImpl(\n    rootLayout: FrameLayout,\n    onBackPressedDispatcher: OnBackPressedDispatcher,\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    val enter: EnterTransition = fadeIn(tween(), 0f),\n    val exit: ExitTransition = fadeOut(tween(), 0f),\n    val content: @Composable AnimatedVisibilityScope.(modal: QMUIModal) -> Unit\n) : QMUIModalPresent(rootLayout, onBackPressedDispatcher, mask, systemCancellable, maskTouchBehavior) {\n\n    @Composable\n    override fun ModalContent(visible: Boolean, dismissFinishAction: () -> Unit) {\n        AnimatedVisibility(\n            visible = visible,\n            enter = enter,\n            exit = exit\n        ) {\n            Box(modifier = Modifier\n                .fillMaxSize()\n                .background(mask)\n                .let {\n                    if (maskTouchBehavior == MaskTouchBehavior.penetrate) {\n                        it\n                    } else {\n                        it.clickable(\n                            interactionSource = remember { MutableInteractionSource() },\n                            indication = null,\n                            enabled = maskTouchBehavior == MaskTouchBehavior.dismiss\n                        ) {\n                            dismiss()\n                        }\n                    }\n                }\n            )\n            content(this@AnimateModalImpl)\n            DisposableEffect(\"\") {\n                onDispose {\n                    dismissFinishAction()\n                }\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "compose/src/main/java/com/qmuiteam/compose/modal/QMUIBottomSheet.kt",
    "content": "package com.qmuiteam.compose.modal\n\nimport android.util.Log\nimport android.view.View\nimport androidx.compose.animation.*\nimport androidx.compose.animation.core.Animatable\nimport androidx.compose.animation.core.VectorConverter\nimport androidx.compose.animation.core.tween\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListScope\nimport androidx.compose.foundation.lazy.LazyListState\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.graphicsLayer\nimport androidx.compose.ui.input.nestedscroll.NestedScrollConnection\nimport androidx.compose.ui.input.nestedscroll.NestedScrollSource\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.layout.onGloballyPositioned\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.Velocity\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun QMUIBottomSheetList(\n    modal: QMUIModal,\n    state: LazyListState = rememberLazyListState(),\n    children: LazyListScope.(QMUIModal) -> Unit\n) {\n    LazyColumn(\n        state = state,\n        modifier = Modifier.fillMaxWidth()\n    ) {\n        children(modal)\n    }\n}\n\n@OptIn(ExperimentalAnimationApi::class)\n@Composable\nfun AnimatedVisibilityScope.QMUIBottomSheet(\n    modal: QMUIModal,\n    draggable: Boolean,\n    widthLimit: (maxWidth: Dp) -> Dp,\n    heightLimit: (maxHeight: Dp) -> Dp,\n    radius: Dp = 2.dp,\n    background: Color = Color.White,\n    mask: Color = DefaultMaskColor,\n    modifier: Modifier,\n    content: @Composable (QMUIModal) -> Unit\n) {\n    BoxWithConstraints(\n        modifier = Modifier.fillMaxSize(),\n        contentAlignment = Alignment.BottomCenter\n    ) {\n\n\n        val wl = widthLimit(maxWidth)\n        val wh = heightLimit(maxHeight)\n\n        var contentModifier = if (wl < maxWidth) {\n            Modifier.width(wl)\n        } else {\n            Modifier.fillMaxWidth()\n        }\n\n        contentModifier = contentModifier\n            .heightIn(max = wh.coerceAtMost(maxHeight))\n\n\n        if (radius > 0.dp) {\n            contentModifier =\n                contentModifier.clip(RoundedCornerShape(topStart = radius, topEnd = radius))\n        }\n        contentModifier = contentModifier\n            .background(background)\n            .clickable(\n                interactionSource = remember { MutableInteractionSource() },\n                indication = null\n            ) {\n\n            }\n\n        if (draggable) {\n            NestScrollWrapper(modal, modifier, mask) {\n                Box(modifier = contentModifier) {\n                    content(modal)\n                }\n            }\n        } else {\n            if (mask != Color.Transparent) {\n                Box(\n                    modifier = Modifier\n                        .fillMaxSize()\n                        .animateEnterExit(\n                            enter = fadeIn(tween()),\n                            exit = fadeOut(tween())\n                        )\n                        .background(mask)\n                )\n            }\n            Box(modifier = modifier.then(contentModifier)) {\n                content(modal)\n            }\n        }\n\n    }\n}\n\n\nprivate class MutableHeight(var height: Float)\n\n@OptIn(ExperimentalAnimationApi::class)\n@Composable\nprivate fun AnimatedVisibilityScope.NestScrollWrapper(\n    modal: QMUIModal,\n    modifier: Modifier,\n    mask: Color,\n    content: @Composable () -> Unit\n) {\n    val yOffsetState = remember {\n        mutableStateOf(0f)\n    }\n\n    val mutableContentHeight = remember {\n        MutableHeight(0f)\n    }\n    val contentHeight = mutableContentHeight.height\n\n    val percent = if (contentHeight <= 0f) 1f else {\n        ((contentHeight - yOffsetState.value) / contentHeight)\n            .coerceAtMost(1f)\n            .coerceAtLeast(0f)\n    }\n\n    val nestedScrollConnection = remember(modal, yOffsetState) {\n        BottomSheetNestedScrollConnection(modal, yOffsetState, mutableContentHeight)\n    }\n\n    val yOffset = yOffsetState.value\n\n    Box(\n        modifier = Modifier.fillMaxSize(),\n        contentAlignment = Alignment.BottomCenter\n    ) {\n        if (mask != Color.Transparent) {\n            Box(\n                modifier = Modifier\n                    .fillMaxSize()\n                    .alpha(percent)\n                    .animateEnterExit(\n                        enter = fadeIn(tween()),\n                        exit = fadeOut(tween())\n                    )\n                    .background(mask)\n            )\n            Box(modifier = modifier\n                .graphicsLayer { translationY = yOffset }\n                .nestedScroll(nestedScrollConnection)\n                .onGloballyPositioned {\n                    mutableContentHeight.height = it.size.height.toFloat()\n                }\n            ) {\n                content()\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalAnimationApi::class)\nfun View.qmuiBottomSheet(\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    draggable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    enter: EnterTransition = slideInVertically(tween()) { it },\n    exit: ExitTransition = slideOutVertically(tween())  { it },\n    widthLimit: (maxWidth: Dp) -> Dp = { it.coerceAtMost(420.dp) },\n    heightLimit: (maxHeight: Dp) -> Dp = { if (it < 640.dp) it - 40.dp else it * 0.85f },\n    radius: Dp = 12.dp,\n    background: Color = Color.White,\n    content: @Composable (QMUIModal) -> Unit\n): QMUIModal {\n    return qmuiModal(\n        Color.Transparent,\n        systemCancellable,\n        maskTouchBehavior,\n        modalHostProvider = modalHostProvider,\n        enter = EnterTransition.None,\n        exit = ExitTransition.None,\n    ) { modal ->\n        QMUIBottomSheet(\n            modal,\n            draggable,\n            widthLimit,\n            heightLimit,\n            radius,\n            background,\n            mask,\n            Modifier.animateEnterExit(\n                enter = enter,\n                exit = exit\n            ),\n            content\n        )\n    }\n}\n\nprivate class BottomSheetNestedScrollConnection(\n    val modal: QMUIModal,\n    val yOffsetStateFlow: MutableState<Float>,\n    val contentHeight: MutableHeight\n) : NestedScrollConnection {\n\n    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {\n        if(source == NestedScrollSource.Fling){\n            return Offset.Zero\n        }\n        val currentOffset = yOffsetStateFlow.value\n        if(available.y < 0 && currentOffset > 0){\n            val consume = available.y.coerceAtLeast(-currentOffset)\n            yOffsetStateFlow.value = currentOffset + consume\n            return Offset(0f, consume)\n        }\n        return super.onPreScroll(available, source)\n    }\n\n    override fun onPostScroll(\n        consumed: Offset,\n        available: Offset,\n        source: NestedScrollSource\n    ): Offset {\n        if(source == NestedScrollSource.Fling){\n            return Offset.Zero\n        }\n        if (available.y > 0) {\n            yOffsetStateFlow.value = yOffsetStateFlow.value + available.y\n            return Offset(0f, available.y)\n        }\n        return super.onPostScroll(consumed, available, source)\n    }\n\n    override suspend fun onPreFling(available: Velocity): Velocity {\n        if (yOffsetStateFlow.value > 0) {\n            if (available.y > 0 || (available.y == 0f && yOffsetStateFlow.value > contentHeight.height / 2)) {\n                modal.dismiss()\n            } else {\n                val animated = Animatable(yOffsetStateFlow.value, Float.VectorConverter)\n                animated.asState()\n                animated.animateTo(0f, tween()){\n                    yOffsetStateFlow.value = value\n                }\n            }\n            return available\n        }\n        return Velocity.Zero\n    }\n}\n"
  },
  {
    "path": "compose/src/main/java/com/qmuiteam/compose/modal/QMUIDialog.kt",
    "content": "package com.qmuiteam.compose.modal\n\nimport android.view.View\nimport androidx.compose.animation.EnterTransition\nimport androidx.compose.animation.ExitTransition\nimport androidx.compose.animation.core.tween\nimport androidx.compose.animation.fadeIn\nimport androidx.compose.animation.fadeOut\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.Indication\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.*\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.Text\nimport androidx.compose.material.ripple.rememberRipple\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.ColorFilter\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.qmuiteam.compose.R\nimport com.qmuiteam.compose.core.ui.*\n\nval DefaultDialogPaddingHor = 20.dp\n\n\n@Composable\nfun QMUIDialog(\n    modal: QMUIModal,\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiDialogVerEdgeProtectionMargin,\n    widthLimit: Dp = 360.dp,\n    radius: Dp = 2.dp,\n    background: Color = Color.White,\n    content: @Composable (QMUIModal) -> Unit\n) {\n    BoxWithConstraints(\n        modifier = Modifier\n            .fillMaxSize()\n            .padding(horizontal = horEdge, vertical = verEdge),\n        contentAlignment = Alignment.Center\n    ) {\n        var modifier = if (widthLimit < maxWidth) {\n            Modifier.width(widthLimit)\n        } else {\n            Modifier.fillMaxWidth()\n        }\n        if (radius > 0.dp) {\n            modifier = modifier.clip(RoundedCornerShape(radius))\n        }\n        modifier = modifier\n            .background(background)\n            .clickable(\n                interactionSource = remember { MutableInteractionSource() },\n                indication = null\n            ) { }\n        Box(modifier = modifier) {\n            content(modal)\n        }\n    }\n}\n\n@Composable\nfun QMUIDialogActions(\n    modal: QMUIModal,\n    actions: List<QMUIModalAction>\n){\n    Row(\n        modifier = Modifier\n            .fillMaxWidth()\n            .padding(start = 6.dp, end = 6.dp, bottom = 8.dp),\n        horizontalArrangement = Arrangement.End\n    ) {\n        actions.forEach {\n            QMUIDialogAction(\n                text = it.text,\n                enabled = it.enabled,\n                color = it.color\n            ) {\n                it.onClick(modal)\n            }\n        }\n    }\n}\n\n@Composable\nfun QMUIDialogMsg(\n    modal: QMUIModal,\n    title: String,\n    content: String,\n    actions: List<QMUIModalAction>\n) {\n    Column {\n        QMUIDialogTitle(title)\n        QMUIDialogMsgContent(content)\n        QMUIDialogActions(modal, actions)\n    }\n}\n\n@Composable\nfun QMUIDialogList(\n    modal: QMUIModal,\n    maxHeight: Dp = Dp.Unspecified,\n    state: LazyListState = rememberLazyListState(),\n    contentPadding: PaddingValues = PaddingValues(vertical = 8.dp),\n    children: LazyListScope.(QMUIModal) -> Unit\n) {\n    LazyColumn(\n        state = state,\n        modifier = Modifier\n            .fillMaxWidth()\n            .heightIn(0.dp, maxHeight),\n        contentPadding = contentPadding\n    ) {\n        children(modal)\n    }\n}\n\n@Composable\nfun QMUIDialogMarkList(\n    modal: QMUIModal,\n    list: List<String>,\n    markIndex: Int,\n    state: LazyListState = rememberLazyListState(markIndex),\n    maxHeight: Dp = Dp.Unspecified,\n    itemIndication: Indication = rememberRipple(color = qmuiIndicationColor),\n    itemTextSize: TextUnit = 17.sp,\n    itemTextColor: Color = qmuiTextMainColor,\n    itemTextFontWeight: FontWeight = FontWeight.Medium,\n    itemTextFontFamily: FontFamily? = null,\n    itemMarkTintColor: Color = qmuiPrimaryColor,\n    contentPadding: PaddingValues = PaddingValues(vertical = 8.dp),\n    onItemClick: (modal: QMUIModal, index: Int) -> Unit\n) {\n    QMUIDialogList(modal, maxHeight, state, contentPadding) {\n        itemsIndexed(list) { index, item ->\n            QMUIItem(\n                title = item,\n                indication = itemIndication,\n                titleOnlyFontSize = itemTextSize,\n                titleColor = itemTextColor,\n                titleFontSize = itemTextSize,\n                titleFontWeight = itemTextFontWeight,\n                titleFontFamily = itemTextFontFamily,\n                accessory = {\n                    if (markIndex == index) {\n                        Image(\n                            painter = painterResource(id = R.drawable.ic_qmui_mark),\n                            contentDescription = \"\",\n                            colorFilter = ColorFilter.tint(itemMarkTintColor)\n                        )\n                    }\n                }\n            ) {\n                onItemClick(modal, index)\n            }\n        }\n    }\n}\n\n\n@Composable\nfun QMUIDialogMutiCheckList(\n    modal: QMUIModal,\n    list: List<String>,\n    checked: Set<Int>,\n    disabled: Set<Int> = emptySet(),\n    disableAlpha: Float = 0.5f,\n    state: LazyListState = rememberLazyListState(0),\n    maxHeight: Dp = Dp.Unspecified,\n    itemIndication: Indication = rememberRipple(color = qmuiIndicationColor),\n    itemTextSize: TextUnit = 17.sp,\n    itemTextColor: Color = qmuiTextMainColor,\n    itemTextFontWeight: FontWeight = FontWeight.Medium,\n    itemTextFontFamily: FontFamily? = null,\n    itemCheckNormalTint: Color = qmuiSeparatorColor,\n    itemCheckCheckedTint: Color = qmuiPrimaryColor,\n    contentPadding: PaddingValues = PaddingValues(vertical = 8.dp),\n    onItemClick: (modal: QMUIModal, index: Int) -> Unit\n) {\n    QMUIDialogList(modal, maxHeight, state, contentPadding) {\n        itemsIndexed(list) { index, item ->\n            val isDisabled = disabled.contains(index)\n            val onClick: (() -> Unit)? = if(isDisabled) null else {\n                {\n                    onItemClick(modal, index)\n                }\n            }\n            QMUIItem(\n                title = item,\n                indication = itemIndication,\n                titleOnlyFontSize = itemTextSize,\n                titleColor = itemTextColor,\n                titleFontSize = itemTextSize,\n                titleFontWeight = itemTextFontWeight,\n                titleFontFamily = itemTextFontFamily,\n                alpha = if(isDisabled) disableAlpha else 1f,\n                accessory = {\n                    if (checked.contains(index)) {\n                        Image(\n                            painter = painterResource(id = R.drawable.ic_qmui_checkbox_checked),\n                            contentDescription = \"\",\n                            colorFilter = ColorFilter.tint(itemCheckCheckedTint)\n                        )\n                    } else {\n                        Image(\n                            painter = painterResource(id = R.drawable.ic_qmui_checkbox_normal),\n                            contentDescription = \"\",\n                            colorFilter = ColorFilter.tint(itemCheckNormalTint)\n                        )\n                    }\n                },\n                onClick =  onClick\n            )\n        }\n    }\n}\n\n@Composable\nfun QMUIDialogTitle(\n    text: String,\n    fontSize: TextUnit = 16.sp,\n    textAlign: TextAlign? = null,\n    color: Color = Color.Black,\n    fontWeight: FontWeight? = FontWeight.Bold,\n    fontFamily: FontFamily? = null,\n    maxLines: Int = Int.MAX_VALUE,\n    lineHeight: TextUnit = 20.sp,\n) {\n    Text(\n        text = text,\n        modifier = Modifier\n            .fillMaxWidth()\n            .padding(\n                top = 24.dp,\n                start = DefaultDialogPaddingHor,\n                end = DefaultDialogPaddingHor,\n            ),\n        textAlign = textAlign,\n        color = color,\n        fontSize = fontSize,\n        fontWeight = fontWeight,\n        fontFamily = fontFamily,\n        maxLines = maxLines,\n        lineHeight = lineHeight\n    )\n}\n\n@Composable\nfun QMUIDialogMsgContent(\n    text: String,\n    fontSize: TextUnit = 14.sp,\n    textAlign: TextAlign? = null,\n    color: Color = Color.Black,\n    fontWeight: FontWeight? = FontWeight.Normal,\n    fontFamily: FontFamily? = null,\n    maxLines: Int = Int.MAX_VALUE,\n    lineHeight: TextUnit = 16.sp,\n) {\n    Text(\n        text = text,\n        modifier = Modifier\n            .fillMaxWidth()\n            .padding(\n                start = DefaultDialogPaddingHor,\n                end = DefaultDialogPaddingHor,\n                top = 16.dp,\n                bottom = 24.dp\n            ),\n        textAlign = textAlign,\n        color = color,\n        fontSize = fontSize,\n        fontWeight = fontWeight,\n        fontFamily = fontFamily,\n        maxLines = maxLines,\n        lineHeight = lineHeight\n    )\n}\n\n@Composable\nfun QMUIDialogAction(\n    text: String,\n    fontSize: TextUnit = 14.sp,\n    color: Color = qmuiPrimaryColor,\n    fontWeight: FontWeight? = FontWeight.Bold,\n    fontFamily: FontFamily? = null,\n    paddingVer: Dp = 9.dp,\n    paddingHor: Dp = 14.dp,\n    enabled: Boolean = true,\n    onClick: () -> Unit\n) {\n    val interactionSource = remember { MutableInteractionSource() }\n    val isPressed = interactionSource.collectIsPressedAsState()\n    Text(\n        text = text,\n        modifier = Modifier\n            .padding(horizontal = paddingHor, vertical = paddingVer)\n            .alpha(if (isPressed.value) 0.5f else 1f)\n            .clickable(\n                enabled = enabled,\n                interactionSource = interactionSource,\n                indication = null\n            ) {\n                onClick.invoke()\n            },\n        color = color,\n        fontSize = fontSize,\n        fontWeight = fontWeight,\n        fontFamily = fontFamily\n    )\n}\n\n\nfun View.qmuiDialog(\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    enter: EnterTransition = fadeIn(tween(), 0f),\n    exit: ExitTransition = fadeOut(tween(), 0f),\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiDialogVerEdgeProtectionMargin,\n    widthLimit: Dp = 360.dp,\n    radius: Dp = 12.dp,\n    background: Color = Color.White,\n    content: @Composable (QMUIModal) -> Unit\n): QMUIModal {\n    return qmuiModal(\n        mask,\n        systemCancellable,\n        maskTouchBehavior,\n        modalHostProvider = modalHostProvider,\n        enter = enter,\n        exit = exit\n    ) { modal ->\n        QMUIDialog(modal, horEdge, verEdge, widthLimit, radius, background, content)\n    }\n}\n\nfun View.qmuiStillDialog(\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    horEdge: Dp = 20.dp,\n    verEdge: Dp = 20.dp,\n    widthLimit: Dp = 360.dp,\n    radius: Dp = 12.dp,\n    background: Color = Color.White,\n    content: @Composable (QMUIModal) -> Unit\n): QMUIModal {\n    return qmuiStillModal(mask, systemCancellable, maskTouchBehavior, modalHostProvider = modalHostProvider) { modal ->\n        QMUIDialog(modal, horEdge, verEdge, widthLimit, radius, background, content)\n    }\n}"
  },
  {
    "path": "compose/src/main/java/com/qmuiteam/compose/modal/QMUIModal.kt",
    "content": "package com.qmuiteam.compose.modal\n\nimport android.os.SystemClock\nimport android.view.View\nimport android.view.Window\nimport android.widget.FrameLayout\nimport androidx.activity.OnBackPressedDispatcher\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.compose.animation.*\nimport androidx.compose.animation.core.tween\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.DisposableEffect\nimport androidx.compose.runtime.DisposableEffectResult\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.LocalView\nimport com.qmuiteam.compose.R\nimport com.qmuiteam.compose.core.ui.qmuiPrimaryColor\n\nval DefaultMaskColor = Color.Black.copy(alpha = 0.5f)\n\nenum class MaskTouchBehavior{\n    dismiss, penetrate, none\n}\n\nprivate class ModalHolder(var current: QMUIModal? = null)\n\nclass QMUIModalAction(\n    val text: String,\n    val enabled: Boolean = true,\n    val color: Color = qmuiPrimaryColor,\n    val onClick: (QMUIModal) -> Unit\n)\n\nprivate class ShowingModals {\n    val modals = mutableMapOf<Long, QMUIModal>()\n}\n\n@Composable\nfun QMUIModal(\n    isVisible: Boolean,\n    mask: Color = DefaultMaskColor,\n    enter: EnterTransition = fadeIn(tween(), 0f),\n    exit: ExitTransition = fadeOut(tween(), 0f),\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    doOnShow: QMUIModal.Action? = null,\n    doOnDismiss: QMUIModal.Action? = null,\n    uniqueId: Long = SystemClock.elapsedRealtimeNanos(),\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    content: @Composable AnimatedVisibilityScope.(QMUIModal) -> Unit\n) {\n    val modalHolder = remember {\n        ModalHolder(null)\n    }\n    if (isVisible) {\n        if (modalHolder.current == null) {\n            val modal = LocalView.current.qmuiModal(\n                mask,\n                systemCancellable,\n                maskTouchBehavior,\n                uniqueId,\n                modalHostProvider,\n                enter,\n                exit,\n                content\n            )\n            doOnShow?.let { modal.doOnShow(it) }\n            doOnDismiss?.let { modal.doOnDismiss(it) }\n            modalHolder.current = modal\n        }\n    } else {\n        modalHolder.current?.dismiss()\n    }\n    DisposableEffect(\"\") {\n        object : DisposableEffectResult {\n            override fun dispose() {\n                modalHolder.current?.dismiss()\n            }\n        }\n    }\n}\n\ninterface QMUIModal {\n    fun show(): QMUIModal\n    fun dismiss()\n    fun isShowing(): Boolean\n\n    fun doOnShow(listener: Action): QMUIModal\n    fun doOnDismiss(listener: Action): QMUIModal\n    fun removeOnShowAction(listener: Action): QMUIModal\n    fun removeOnDismissAction(listener: Action): QMUIModal\n\n    fun interface Action {\n        fun invoke(modal: QMUIModal)\n    }\n}\n\nfun interface ModalHostProvider {\n    fun provide(view: View): Pair<FrameLayout, OnBackPressedDispatcher>\n}\n\nclass ActivityHostModalProvider : ModalHostProvider {\n    override fun provide(view: View): Pair<FrameLayout, OnBackPressedDispatcher> {\n        val contentLayout =\n            view.rootView.findViewById<FrameLayout>(Window.ID_ANDROID_CONTENT) ?: throw RuntimeException(\"View is not attached to Activity\")\n        val activity = contentLayout.context as? AppCompatActivity ?: throw RuntimeException(\"view's rootView's context is not AppCompatActivity\")\n        return contentLayout to activity.onBackPressedDispatcher\n    }\n}\n\nval DefaultModalHostProvider = ActivityHostModalProvider()\n\nfun View.qmuiModal(\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    uniqueId: Long = SystemClock.elapsedRealtimeNanos(),\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    enter: EnterTransition = fadeIn(tween(), 0f),\n    exit: ExitTransition = fadeOut(tween(), 0f),\n    content: @Composable AnimatedVisibilityScope.(QMUIModal) -> Unit\n): QMUIModal {\n    if (!isAttachedToWindow) {\n        throw RuntimeException(\"View is not attached to window\")\n    }\n    val modalHost = modalHostProvider.provide(this)\n    val modal = AnimateModalImpl(\n        modalHost.first,\n        modalHost.second,\n        mask,\n        systemCancellable,\n        maskTouchBehavior,\n        enter,\n        exit,\n        content\n    )\n    val hostView = modalHost.first\n    handleModelUnique(hostView, modal, uniqueId)\n    return modal\n}\n\nfun View.qmuiStillModal(\n    mask: Color = DefaultMaskColor,\n    systemCancellable: Boolean = true,\n    maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.dismiss,\n    uniqueId: Long = SystemClock.elapsedRealtimeNanos(),\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    content: @Composable (QMUIModal) -> Unit\n): QMUIModal {\n    if (!isAttachedToWindow) {\n        throw RuntimeException(\"View is not attached to window\")\n    }\n    val modalHost = modalHostProvider.provide(this)\n    val modal = StillModalImpl(modalHost.first, modalHost.second, mask, systemCancellable, maskTouchBehavior, content)\n    val hostView = modalHost.first\n    handleModelUnique(hostView, modal, uniqueId)\n    return modal\n}\n\nprivate fun handleModelUnique(hostView: FrameLayout, modal: QMUIModal, uniqueId: Long) {\n    val showingModals = (hostView.getTag(R.id.qmui_modals) as? ShowingModals) ?: ShowingModals().also {\n        hostView.setTag(R.id.qmui_modals, it)\n    }\n\n    modal.doOnShow {\n        showingModals.modals.put(uniqueId, it)?.dismiss()\n    }\n\n    modal.doOnDismiss {\n        if (showingModals.modals[uniqueId] == it) {\n            showingModals.modals.remove(uniqueId)\n        }\n    }\n}"
  },
  {
    "path": "compose/src/main/java/com/qmuiteam/compose/modal/QMUIToast.kt",
    "content": "package com.qmuiteam.compose.modal\n\nimport android.view.View\nimport androidx.compose.animation.*\nimport androidx.compose.animation.core.tween\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.qmuiteam.compose.core.ui.qmuiCommonHorSpace\nimport com.qmuiteam.compose.core.ui.qmuiToastVerEdgeProtectionMargin\nimport kotlinx.coroutines.*\n\nprivate val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)\n\n@Composable\nfun QMUIToast(\n    modal: QMUIModal,\n    radius: Dp = 8.dp,\n    background: Color = Color.DarkGray,\n    content: @Composable BoxScope.(QMUIModal) -> Unit\n) {\n    Box(\n        modifier = Modifier\n            .clip(RoundedCornerShape(radius))\n            .background(background)\n    ) {\n        content(modal)\n    }\n}\n\nfun View.qmuiToast(\n    text: String,\n    textColor: Color = Color.White,\n    fontSize: TextUnit = 16.sp,\n    duration: Long = 1000,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    alignment: Alignment = Alignment.BottomCenter,\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiToastVerEdgeProtectionMargin,\n    radius: Dp = 8.dp,\n    background: Color = Color.Black,\n    enter: EnterTransition = slideInVertically(initialOffsetY = { it }) + fadeIn(),\n    exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),\n): QMUIModal {\n    return qmuiToast(\n        duration,\n        modalHostProvider,\n        alignment,\n        horEdge,\n        verEdge,\n        radius,\n        background,\n        enter,\n        exit\n    ) {\n        Text(\n            text = text,\n            color = textColor,\n            fontSize = fontSize,\n            modifier = Modifier\n                .padding(horizontal = 24.dp, vertical = 16.dp)\n                .align(Alignment.Center)\n        )\n    }\n}\n\n@OptIn(ExperimentalAnimationApi::class)\nfun View.qmuiToast(\n    duration: Long = 1000,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    alignment: Alignment = Alignment.BottomCenter,\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiToastVerEdgeProtectionMargin,\n    radius: Dp = 8.dp,\n    background: Color = Color.Black,\n    enter: EnterTransition = slideInVertically(initialOffsetY = { it }) + fadeIn(),\n    exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),\n    content: @Composable BoxScope.(QMUIModal) -> Unit\n): QMUIModal {\n    var job: Job? = null\n    return qmuiModal(\n        Color.Transparent,\n        false,\n        MaskTouchBehavior.penetrate,\n        -1,\n        modalHostProvider,\n        enter = EnterTransition.None,\n        exit = ExitTransition.None\n    ) { modal ->\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(horizontal = horEdge, vertical = verEdge),\n            contentAlignment = alignment\n        ) {\n            Box(\n                modifier = Modifier\n                    .animateEnterExit(\n                        enter = enter,\n                        exit = exit\n                    )\n            ) {\n                QMUIToast(modal, radius, background, content)\n            }\n        }\n    }.doOnShow {\n        job = scope.launch {\n            delay(duration)\n            job = null\n            it.dismiss()\n        }\n    }.doOnDismiss {\n        job?.cancel()\n        job = null\n    }.show()\n}\n\nfun View.qmuiStillToast(\n    text: String,\n    textColor: Color = Color.White,\n    fontSize: TextUnit = 16.sp,\n    duration: Long = 1000,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    alignment: Alignment = Alignment.BottomCenter,\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiToastVerEdgeProtectionMargin,\n    radius: Dp = 8.dp,\n    background: Color = Color.Black\n): QMUIModal {\n    return qmuiStillToast(\n        duration,\n        modalHostProvider,\n        alignment,\n        horEdge,\n        verEdge,\n        radius,\n        background\n    ) {\n        Text(\n            text = text,\n            color = textColor,\n            fontSize = fontSize,\n            modifier = Modifier\n                .padding(horizontal = 24.dp, vertical = 16.dp)\n                .align(Alignment.Center)\n        )\n    }\n}\n\n@OptIn(ExperimentalAnimationApi::class)\nfun View.qmuiStillToast(\n    duration: Long = 1000,\n    modalHostProvider: ModalHostProvider = DefaultModalHostProvider,\n    alignment: Alignment = Alignment.BottomCenter,\n    horEdge: Dp = qmuiCommonHorSpace,\n    verEdge: Dp = qmuiToastVerEdgeProtectionMargin,\n    radius: Dp = 8.dp,\n    background: Color = Color.Black,\n    content: @Composable BoxScope.(QMUIModal) -> Unit\n): QMUIModal {\n    var job: Job? = null\n    return qmuiStillModal(\n        Color.Transparent,\n        false,\n        MaskTouchBehavior.penetrate,\n        -1,\n        modalHostProvider,\n    ) { modal ->\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(horizontal = horEdge, vertical = verEdge),\n            contentAlignment = alignment\n        ) {\n            QMUIToast(modal, radius, background, content)\n        }\n    }.doOnShow {\n        job = scope.launch {\n            delay(duration)\n            job = null\n            it.dismiss()\n        }\n    }.doOnDismiss {\n        job?.cancel()\n        job = null\n    }.show()\n}"
  },
  {
    "path": "compose/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"qmui_modals\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "compose/src/test/java/com/qmuiteam/compose/ExampleUnitTest.kt",
    "content": "package com.qmuiteam.compose\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n\n}"
  },
  {
    "path": "compose-core/.gitignore",
    "content": "/build"
  },
  {
    "path": "compose-core/build.gradle.kts",
    "content": "\nimport com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.composeCoreVer\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    buildFeatures {\n        compose = true\n    }\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n}\n\ndependencies {\n    api(Dep.AndroidX.appcompat)\n    api(Dep.Compose.ui)\n    api(Dep.Compose.animation)\n    api(Dep.Compose.material)\n    api(Dep.Compose.compiler)\n}"
  },
  {
    "path": "compose-core/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "compose-core/src/androidTest/java/com/qmuiteam/compose/ExampleInstrumentedTest.kt",
    "content": "package com.qmuiteam.compose\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.qmuiteam.compose\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "compose-core/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qmuiteam.compose.core\">\n\n</manifest>"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ex/DrawScopeEx.kt",
    "content": "package com.qmuiteam.compose.core.ex\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.StrokeCap\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport com.qmuiteam.compose.core.ui.qmuiSeparatorColor\n\nfun DrawScope.drawTopSeparator(color: Color = qmuiSeparatorColor, insetStart: Dp = 0.dp, insetEnd: Dp = 0.dp) {\n    drawLine(\n        color = color,\n        start = Offset(insetStart.toPx(), 0f),\n        end = Offset(size.width - insetEnd.toPx(), 0f),\n        cap = StrokeCap.Square\n    )\n}\n\nfun DrawScope.drawBottomSeparator(color: Color = qmuiSeparatorColor, insetStart: Dp = 0.dp, insetEnd: Dp = 0.dp) {\n    drawLine(\n        color = color,\n        start = Offset(insetStart.toPx(), size.height),\n        end = Offset(size.width - insetEnd.toPx(), size.height),\n        cap = StrokeCap.Square\n    )\n}\n\nfun DrawScope.drawLeftSeparator(color: Color = qmuiSeparatorColor, insetStart: Dp = 0.dp, insetEnd: Dp = 0.dp) {\n    drawLine(\n        color = color,\n        start = Offset(0f, insetStart.toPx()),\n        end = Offset(0f, size.height - insetEnd.toPx()),\n        cap = StrokeCap.Square\n    )\n}\n\nfun DrawScope.drawRightSeparator(color: Color = qmuiSeparatorColor, insetStart: Dp = 0.dp, insetEnd: Dp = 0.dp) {\n    drawLine(\n        color = color,\n        start = Offset(size.width, insetStart.toPx()),\n        end = Offset(size.width, size.height - insetEnd.toPx()),\n        cap = StrokeCap.Square\n    )\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/helper/Dimen.kt",
    "content": "package com.qmuiteam.compose.core.helper\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun OnePx(): Dp {\n    return (1 / LocalDensity.current.density).dp\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/helper/Global.kt",
    "content": "package com.qmuiteam.compose.core.helper\n\nobject QMUIGlobal {\n    var debug: Boolean = false\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/helper/Log.kt",
    "content": "package com.qmuiteam.compose.core.helper\n\nimport android.util.Log\n\ninterface QMUILogDelegate {\n    fun e(tag: String, msg: String, throwable: Throwable? = null)\n    fun w(tag: String, msg: String, throwable: Throwable? = null)\n    fun i(tag: String, msg: String, throwable: Throwable? = null)\n    fun d(tag: String, msg: String, throwable: Throwable? = null)\n}\n\nobject SystemLogDelegate : QMUILogDelegate {\n\n    override fun e(tag: String, msg: String, throwable: Throwable?) {\n        Log.e(tag, msg, throwable)\n    }\n\n    override fun w(tag: String, msg: String, throwable: Throwable?) {\n        Log.w(tag, msg, throwable)\n    }\n\n    override fun i(tag: String, msg: String, throwable: Throwable?) {\n        Log.i(tag, msg, throwable)\n    }\n\n    override fun d(tag: String, msg: String, throwable: Throwable?) {\n        Log.d(tag, msg, throwable)\n    }\n}\n\nobject QMUILog {\n\n    var delegate: QMUILogDelegate? = SystemLogDelegate\n\n    fun e(tag: String, msg: String, throwable: Throwable? = null) {\n        delegate?.e(tag, msg, throwable)\n    }\n\n    fun w(tag: String, msg: String, throwable: Throwable? = null) {\n        delegate?.w(tag, msg, throwable)\n    }\n\n    fun i(tag: String, msg: String, throwable: Throwable? = null) {\n        delegate?.i(tag, msg, throwable)\n    }\n\n    fun d(tag: String, msg: String, throwable: Throwable? = null) {\n        delegate?.d(tag, msg, throwable)\n    }\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/helper/LogTag.kt",
    "content": "package com.qmuiteam.compose.core.helper\n\ninterface LogTag {\n    val TAG: String\n        get() = getTag(javaClass)\n}\n\nfun logTag(clazz: Class<*>): LogTag = object : LogTag {\n    override val TAG = getTag(clazz)\n}\n\ninline fun <reified T: Any> logTag(): LogTag = logTag(T::class.java)\n\nprivate fun getTag(clazz: Class<*>): String {\n    val tag = clazz.simpleName\n    return if (tag.length <= 23) {\n        tag\n    } else {\n        tag.substring(0, 23)\n    }\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/provider/WindowInsets.kt",
    "content": "package com.qmuiteam.compose.core.provider\n\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.platform.LocalView\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.core.graphics.Insets\nimport androidx.core.view.OnApplyWindowInsetsListener\nimport androidx.core.view.ViewCompat\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.R\n\nval QMUILocalWindowInsets = staticCompositionLocalOf { WindowInsetsCompat.CONSUMED }\n\n@Composable\nfun QMUIWindowInsetsProvider(content: @Composable () -> Unit) {\n    val view = LocalView.current\n    val windowInsets = remember(view) {\n        mutableStateOf(view.getTag(R.id.qmui_window_inset_cache) as? WindowInsetsCompat ?: WindowInsetsCompat.CONSUMED)\n    }\n    LaunchedEffect(view) {\n        ViewCompat.setOnApplyWindowInsetsListener(view, OnApplyWindowInsetsListener { _, insets ->\n            windowInsets.value = insets\n            view.setTag(R.id.qmui_window_inset_cache, insets)\n            return@OnApplyWindowInsetsListener insets\n        })\n        view.requestApplyInsets()\n    }\n    CompositionLocalProvider(QMUILocalWindowInsets provides windowInsets.value) {\n        content()\n    }\n}\n\ndata class DpInsets(val left: Dp, val top: Dp, val right: Dp, val bottom: Dp) {\n    companion object {\n        val NONE = DpInsets(0.dp, 0.dp, 0.dp, 0.dp)\n    }\n}\n\n@Composable\nfun Insets.dp(): DpInsets {\n    if (this == Insets.NONE) {\n        return DpInsets.NONE\n    }\n    return with(LocalDensity.current) {\n        DpInsets(\n            (left / density).dp,\n            (top / density).dp,\n            (right / density).dp,\n            (bottom / density).dp\n        )\n    }\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ui/DefaultConfig.kt",
    "content": "package com.qmuiteam.compose.core.ui\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.unit.dp\n\nval qmuiPrimaryColor = Color(0xFF00A8E1)\nval qmuiSeparatorColor = Color(0xFFCCCCCC)\nval qmuiIndicationColor = Color(0xFF777777)\nval qmuiTextMainColor = Color.Black\nval qmuiTextDescColor = Color(0xFF666666)\n\n\n\nval qmuiTopBarHeight = 48.dp\nval qmuiTopBarZIndex = 32f\nval qmuiCommonHorSpace = 20.dp\nval qmuiScrollAlphaChangeMaxOffset = 20.dp\n\n\nval qmuiDialogVerEdgeProtectionMargin = 44.dp\nval qmuiToastVerEdgeProtectionMargin = 96.dp\n\n\n\n"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ui/PressWithAlphaBox.kt",
    "content": "package com.qmuiteam.compose.core.ui\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxScope\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\n\n@Composable\nfun PressWithAlphaBox(\n    modifier: Modifier = Modifier,\n    enable: Boolean = true,\n    pressAlpha: Float = 0.5f,\n    disableAlpha: Float = 0.5f,\n    onClick: (() -> Unit)? = null,\n    content: @Composable BoxScope.() -> Unit\n) {\n    val interactionSource = remember { MutableInteractionSource() }\n    val isPressed = interactionSource.collectIsPressedAsState()\n    Box(modifier = Modifier\n        .alpha(if (!enable) disableAlpha else if (isPressed.value) pressAlpha else 1f)\n        .clickable(enabled = enable, interactionSource = interactionSource, indication = null) {\n            onClick?.invoke()\n        }\n        .then(modifier),\n        content = content\n    )\n\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ui/QMUIIcon.kt",
    "content": "package com.qmuiteam.compose.core.ui\n\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.animation.fadeIn\nimport androidx.compose.animation.fadeOut\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.ColorFilter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.unit.Dp\nimport com.qmuiteam.compose.core.R\n\n@Composable\nfun QMUIChevronIcon(tint: Color? = null) {\n    Image(\n        painter = painterResource(id = R.drawable.ic_qmui_chevron),\n        contentDescription = \"\",\n        colorFilter = tint?.let { ColorFilter.tint(it) }\n    )\n}\n\nenum class CheckStatus {\n    none, partial, checked\n}\n\n\n@Composable\nfun QMUICheckBox(\n    size: Dp,\n    status: CheckStatus = CheckStatus.none,\n    isEnabled: Boolean = true,\n    tint: Color?,\n    background: Color = Color.Transparent\n) {\n    Box(\n        modifier = Modifier\n            .size(size)\n            .clip(CircleShape)\n    ) {\n        AnimatedVisibility(\n            visible = status == CheckStatus.none,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUICheckBoxImage(R.drawable.ic_qmui_checkbox_normal, isEnabled, tint, background)\n        }\n\n        AnimatedVisibility(\n            visible = status == CheckStatus.checked,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUICheckBoxImage(R.drawable.ic_qmui_checkbox_checked, isEnabled, tint, background)\n        }\n\n        AnimatedVisibility(\n            visible = status == CheckStatus.partial,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUICheckBoxImage(R.drawable.ic_qmui_checkbox_partial, isEnabled, tint, background)\n        }\n    }\n}\n\n@Composable\nprivate fun QMUICheckBoxImage(\n    resourceId: Int,\n    isEnabled: Boolean = true,\n    tint: Color?,\n    background: Color = Color.Transparent\n){\n    Image(\n        painter = painterResource(id = resourceId),\n        contentScale = ContentScale.Fit,\n        contentDescription = \"\",\n        colorFilter = tint?.let { ColorFilter.tint(it) },\n        modifier = Modifier\n            .fillMaxSize()\n            .let {\n                if (isEnabled) {\n                    it\n                } else {\n                    it.alpha(0.5f)\n                }\n            }.let {\n                if (background != Color.Transparent) {\n                    it.background(background)\n                } else {\n                    it\n                }\n            }\n    )\n}\n\n@Composable\nfun QMUIMarkIcon(\n    modifier: Modifier = Modifier,\n    tint: Color? = null\n) {\n    Image(\n        painter = painterResource(id = R.drawable.ic_qmui_mark),\n        contentDescription = \"\",\n        colorFilter = tint?.let { ColorFilter.tint(it) },\n        modifier = modifier\n    )\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ui/QMUIItem.kt",
    "content": "package com.qmuiteam.compose.core.ui\n\nimport androidx.compose.foundation.Indication\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.material.Text\nimport androidx.compose.material.ripple.rememberRipple\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.drawBehind\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\n\n\n@Composable\nfun QMUIItem(\n    title: String,\n    detail: String = \"\",\n    alpha: Float = 1f,\n    background: Color = Color.Transparent,\n    indication: Indication = rememberRipple(color = qmuiIndicationColor),\n    titleFontSize: TextUnit = 16.sp,\n    titleOnlyFontSize: TextUnit = 17.sp,\n    titleColor: Color = qmuiTextMainColor,\n    titleFontWeight: FontWeight = FontWeight.Medium,\n    titleFontFamily: FontFamily? = null,\n    titleLineHeight: TextUnit = 20.sp,\n    detailFontSize: TextUnit = 12.sp,\n    detailColor: Color = qmuiTextDescColor,\n    detailFontWeight: FontWeight = FontWeight.Normal,\n    detailFontFamily: FontFamily? = null,\n    detailLineHeight: TextUnit = 17.sp,\n    minHeight: Dp = 56.dp,\n    paddingHor: Dp = qmuiCommonHorSpace,\n    paddingVer: Dp = 12.dp,\n    gapBetweenTitleAndDetail: Dp = 4.dp,\n    accessory: @Composable (RowScope.() -> Unit)? = null,\n    drawBehind: (DrawScope.() -> Unit)? = null,\n    onClick: (() -> Unit)? = null\n) {\n    Row(modifier = Modifier\n        .fillMaxWidth()\n        .defaultMinSize(minHeight = minHeight)\n        .alpha(alpha)\n        .background(background)\n        .drawBehind {\n            drawBehind?.invoke(this)\n        }\n        .clickable(\n            enabled = onClick != null,\n            interactionSource = remember { MutableInteractionSource() },\n            indication = indication\n        ) {\n            onClick?.invoke()\n        }\n        .padding(horizontal = paddingHor, vertical = paddingVer),\n        verticalAlignment = Alignment.CenterVertically\n    ) {\n        Column(modifier = Modifier.weight(1f)) {\n            Text(\n                text = title,\n                color = titleColor,\n                modifier = Modifier.fillMaxWidth(),\n                fontSize = if (detail.isNotBlank()) titleFontSize else titleOnlyFontSize,\n                fontWeight = titleFontWeight,\n                fontFamily = titleFontFamily,\n                lineHeight = titleLineHeight\n            )\n            if (detail.isNotBlank()) {\n                Text(\n                    text = detail,\n                    color = detailColor,\n                    modifier = Modifier\n                        .fillMaxWidth()\n                        .padding(top = gapBetweenTitleAndDetail),\n                    fontSize = detailFontSize,\n                    fontWeight = detailFontWeight,\n                    fontFamily = detailFontFamily,\n                    lineHeight = detailLineHeight\n                )\n            }\n\n        }\n        accessory?.invoke(this)\n    }\n}"
  },
  {
    "path": "compose-core/src/main/java/com/qmuiteam/compose/core/ui/QMUITopBar.kt",
    "content": "package com.qmuiteam.compose.core.ui\n\nimport androidx.annotation.DrawableRes\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyListState\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.*\nimport androidx.compose.ui.layout.*\nimport androidx.compose.ui.platform.InspectorInfo\nimport androidx.compose.ui.platform.InspectorValueInfo\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.platform.debugInspectorInfo\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.unit.*\nimport androidx.compose.ui.zIndex\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.R\nimport com.qmuiteam.compose.core.helper.OnePx\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\n\nfun interface QMUITopBarItem {\n    @Composable\n    fun Compose(topBarHeight: Dp)\n}\n\ninterface QMUITopBarTitleLayout {\n    @Composable\n    fun Compose(title: CharSequence, subTitle: CharSequence, alignTitleCenter: Boolean)\n}\n\nclass DefaultQMUITopBarTitleLayout(\n    val titleColor: Color = Color.White,\n    val titleFontWeight: FontWeight = FontWeight.Bold,\n    val titleFontFamily: FontFamily? = null,\n    val titleFontSize: TextUnit = 16.sp,\n    val titleOnlyFontSize: TextUnit = 17.sp,\n    val subTitleColor: Color = Color.White.copy(alpha = 0.8f),\n    val subTitleFontWeight: FontWeight = FontWeight.Normal,\n    val subTitleFontFamily: FontFamily? = null,\n    val subTitleFontSize: TextUnit = 11.sp\n\n) : QMUITopBarTitleLayout {\n    @Composable\n    override fun Compose(title: CharSequence, subTitle: CharSequence, alignTitleCenter: Boolean) {\n        Column(\n            modifier = Modifier.fillMaxWidth(),\n            horizontalAlignment = if (alignTitleCenter) Alignment.CenterHorizontally else Alignment.Start\n        ) {\n            Text(\n                title.toString(),\n                color = titleColor,\n                fontWeight = titleFontWeight,\n                fontFamily = titleFontFamily,\n                fontSize = if (subTitle.isNotEmpty()) titleFontSize else titleOnlyFontSize,\n                overflow = TextOverflow.Ellipsis,\n                maxLines = 1\n            )\n            if (subTitle.isNotEmpty()) {\n                Text(\n                    subTitle.toString(),\n                    color = subTitleColor,\n                    fontWeight = subTitleFontWeight,\n                    fontFamily = subTitleFontFamily,\n                    fontSize = subTitleFontSize,\n                    overflow = TextOverflow.Ellipsis,\n                    maxLines = 1\n                )\n            }\n        }\n    }\n}\n\nopen class QMUITopBarBackIconItem(\n    tint: Color = Color.White,\n    pressAlpha: Float = 0.5f,\n    disableAlpha: Float = 0.5f,\n    enable: Boolean = true,\n    onClick: () -> Unit\n) : QMUITopBarIconItem(\n    R.drawable.ic_qmui_topbar_back,\n    \"返回\",\n    tint,\n    pressAlpha,\n    disableAlpha,\n    enable,\n    onClick\n)\n\nopen class QMUITopBarIconItem(\n    @DrawableRes val icon: Int,\n    val contentDescription: String = \"\",\n    val tint: Color = Color.White,\n    val pressAlpha: Float = 0.5f,\n    val disableAlpha: Float = 0.5f,\n    val enable: Boolean = true,\n    val onClick: () -> Unit\n) : QMUITopBarItem {\n\n    @Composable\n    override fun Compose(topBarHeight: Dp) {\n        PressWithAlphaBox(\n            modifier = Modifier.size(topBarHeight),\n            enable = enable,\n            pressAlpha = pressAlpha,\n            disableAlpha = disableAlpha,\n            onClick = onClick\n        ) {\n            Image(\n                modifier = Modifier.fillMaxSize(),\n                painter = painterResource(icon),\n                contentDescription = contentDescription,\n                colorFilter = ColorFilter.tint(tint),\n                contentScale = ContentScale.Inside\n            )\n        }\n    }\n\n}\n\n\nopen class QMUITopBarTextItem(\n    val text: String,\n    val paddingHor: Dp = 12.dp,\n    val fontSize: TextUnit = 14.sp,\n    val fontWeight: FontWeight = FontWeight.Medium,\n    val color: Color = Color.White,\n    val pressAlpha: Float = 0.5f,\n    val disableAlpha: Float = 0.5f,\n    val enable: Boolean = true,\n    val onClick: () -> Unit\n) : QMUITopBarItem {\n\n    @Composable\n    override fun Compose(topBarHeight: Dp) {\n        PressWithAlphaBox(\n            modifier = Modifier\n                .height(topBarHeight)\n                .padding(horizontal = paddingHor),\n            enable = enable,\n            pressAlpha = pressAlpha,\n            disableAlpha = disableAlpha,\n            onClick = onClick\n        ) {\n            Text(\n                text = text,\n                modifier = Modifier.align(Alignment.Center),\n                color = color,\n                fontSize = fontSize,\n                fontWeight = fontWeight\n            )\n        }\n    }\n\n}\n\n@Composable\nfun QMUITopBarWithLazyScrollState(\n    scrollState: LazyListState,\n    title: CharSequence = \"\",\n    subTitle: CharSequence = \"\",\n    alignTitleCenter: Boolean = true,\n    height: Dp = qmuiTopBarHeight,\n    zIndex: Float = qmuiTopBarZIndex,\n    backgroundColor: Color = qmuiPrimaryColor,\n    changeWithBackground: Boolean = false,\n    scrollAlphaChangeMaxOffset: Dp = qmuiScrollAlphaChangeMaxOffset,\n    shadowElevation: Dp = 16.dp,\n    shadowAlpha: Float = 0.6f,\n    separatorHeight: Dp = OnePx(),\n    separatorColor: Color = qmuiSeparatorColor,\n    paddingStart: Dp = 4.dp,\n    paddingEnd: Dp = 4.dp,\n    titleBoxPaddingHor: Dp = 8.dp,\n    leftItems: List<QMUITopBarItem> = emptyList(),\n    rightItems: List<QMUITopBarItem> = emptyList(),\n    titleLayout: QMUITopBarTitleLayout = remember { DefaultQMUITopBarTitleLayout() }\n){\n    val percent = with(LocalDensity.current){\n        if(scrollState.firstVisibleItemIndex > 0 || scrollState.firstVisibleItemScrollOffset.toDp() > scrollAlphaChangeMaxOffset){\n            1f\n        } else scrollState.firstVisibleItemScrollOffset.toDp() / scrollAlphaChangeMaxOffset\n    }\n    QMUITopBar(\n        title, subTitle,\n        alignTitleCenter, height, zIndex,\n        if(changeWithBackground) backgroundColor.copy(backgroundColor.alpha * percent) else backgroundColor,\n        shadowElevation, shadowAlpha * percent,\n        separatorHeight, separatorColor.copy(separatorColor.alpha * percent),\n        paddingStart, paddingEnd,\n        titleBoxPaddingHor, leftItems, rightItems, titleLayout\n    )\n}\n\n@Composable\nfun QMUITopBar(\n    title: CharSequence,\n    subTitle: CharSequence = \"\",\n    alignTitleCenter: Boolean = true,\n    height: Dp = qmuiTopBarHeight,\n    zIndex: Float = qmuiTopBarZIndex,\n    backgroundColor: Color = qmuiPrimaryColor,\n    shadowElevation: Dp = 16.dp,\n    shadowAlpha: Float = 0.4f,\n    separatorHeight: Dp = OnePx(),\n    separatorColor: Color = qmuiSeparatorColor,\n    paddingStart: Dp = 4.dp,\n    paddingEnd: Dp = 4.dp,\n    titleBoxPaddingHor: Dp = 8.dp,\n    leftItems: List<QMUITopBarItem> = emptyList(),\n    rightItems: List<QMUITopBarItem> = emptyList(),\n    titleLayout: QMUITopBarTitleLayout = remember { DefaultQMUITopBarTitleLayout() }\n) {\n    val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n        WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.displayCutout()\n    ).dp()\n    Box(modifier = Modifier\n        .fillMaxWidth()\n        .height(IntrinsicSize.Max)\n        .zIndex(zIndex)\n    ){\n        Box(modifier = Modifier.fillMaxSize().graphicsLayer {\n            this.alpha = shadowAlpha\n            this.shadowElevation = shadowElevation.toPx()\n            this.shape =  RectangleShape\n            this.clip = shadowElevation > 0.dp\n        })\n        Box(\n            modifier = Modifier\n                .fillMaxWidth()\n                .background(backgroundColor)\n                .padding(top = insets.top)\n                .height(height)\n        ) {\n            QMUITopBarContent(\n                title,\n                subTitle,\n                alignTitleCenter,\n                height,\n                paddingStart,\n                paddingEnd,\n                titleBoxPaddingHor,\n                leftItems,\n                rightItems,\n                titleLayout\n            )\n            if(separatorHeight > 0.dp && separatorColor != Color.Transparent){\n                Box(modifier = Modifier\n                    .fillMaxWidth()\n                    .height(separatorHeight)\n                    .align(Alignment.BottomStart)\n                    .background(separatorColor)\n                )\n            }\n        }\n    }\n\n}\n\n@Composable\nfun QMUITopBarContent(\n    title: CharSequence,\n    subTitle: CharSequence,\n    alignTitleCenter: Boolean,\n    height: Dp = qmuiTopBarHeight,\n    paddingStart: Dp = 4.dp,\n    paddingEnd: Dp = 4.dp,\n    titleBoxPaddingHor: Dp = 8.dp,\n    leftItems: List<QMUITopBarItem> = emptyList(),\n    rightItems: List<QMUITopBarItem> = emptyList(),\n    titleLayout: QMUITopBarTitleLayout = remember { DefaultQMUITopBarTitleLayout() }\n) {\n\n    val measurePolicy = remember(alignTitleCenter) {\n        MeasurePolicy { measurables, constraints ->\n            var centerMeasurable: Measurable? = null\n            var leftPlaceable: Placeable? = null\n            var rightPlaceable: Placeable? = null\n            var centerPlaceable: Placeable? = null\n            val usedConstraints = constraints.copy(minWidth = 0)\n            measurables\n                .forEach {\n                when ((it.parentData as? QMUITopBarAreaParentData)?.area ?: QMUITopBarArea.Left) {\n                    QMUITopBarArea.Left -> {\n                        leftPlaceable = it.measure(usedConstraints)\n                    }\n                    QMUITopBarArea.Right -> {\n                        rightPlaceable = it.measure(usedConstraints)\n                    }\n                    QMUITopBarArea.Center -> {\n                        centerMeasurable = it\n                    }\n                }\n            }\n            val leftItemsWidth = leftPlaceable?.measuredWidth ?: 0\n            val rightItemsWidth = rightPlaceable?.measuredWidth ?: 0\n            val itemsWidthMax = maxOf(leftItemsWidth, rightItemsWidth)\n            val titleContainerWidth = if (alignTitleCenter) {\n                constraints.maxWidth - itemsWidthMax * 2\n            } else {\n                constraints.maxWidth - leftItemsWidth - rightItemsWidth\n            }\n            if (titleContainerWidth > 0) {\n                centerPlaceable = centerMeasurable?.measure(constraints.copy(minWidth = 0, maxWidth = titleContainerWidth))\n            }\n\n            layout(constraints.maxWidth, constraints.maxHeight) {\n                leftPlaceable?.place(0, 0, 0f)\n                rightPlaceable?.let {\n                    it.place(constraints.maxWidth - it.measuredWidth, 0, 1f)\n                }\n                centerPlaceable?.let {\n                    if (alignTitleCenter) {\n                        it.place(itemsWidthMax, 0, 2f)\n                    } else {\n                        it.place(leftItemsWidth, 0, 2f)\n                    }\n                }\n            }\n        }\n    }\n    Layout(\n        content = {\n            Row(\n                modifier = Modifier\n                    .fillMaxHeight()\n                    .qmuiTopBarArea(QMUITopBarArea.Left),\n                verticalAlignment = Alignment.CenterVertically\n            ) {\n                leftItems.forEach {\n                    it.Compose(height)\n                }\n            }\n\n            Box(\n                modifier = Modifier\n                    .fillMaxHeight()\n                    .qmuiTopBarArea(QMUITopBarArea.Center)\n                    .padding(horizontal = titleBoxPaddingHor),\n                contentAlignment = Alignment.CenterStart\n            ) {\n                titleLayout.Compose(title, subTitle, alignTitleCenter)\n            }\n\n            Row(\n                modifier = Modifier\n                    .fillMaxHeight()\n                    .qmuiTopBarArea(QMUITopBarArea.Right),\n                verticalAlignment = Alignment.CenterVertically\n            ) {\n                rightItems.forEach {\n                    it.Compose(height)\n                }\n            }\n\n        },\n        measurePolicy = measurePolicy,\n        modifier = Modifier\n            .fillMaxWidth()\n            .height(height)\n            .padding(start = paddingStart, end = paddingEnd)\n    )\n}\n\n\ninternal enum class QMUITopBarArea { Left, Center, Right }\n\ninternal data class QMUITopBarAreaParentData(\n    var area: QMUITopBarArea = QMUITopBarArea.Left\n)\n\ninternal fun Modifier.qmuiTopBarArea(area: QMUITopBarArea) = this.then(\n    QMUITopBarAreaModifier(\n        area = area,\n        inspectorInfo = debugInspectorInfo {\n            name = \"area\"\n            value = area.name\n        }\n    )\n)\n\ninternal class QMUITopBarAreaModifier(\n    val area: QMUITopBarArea,\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    override fun Density.modifyParentData(parentData: Any?): QMUITopBarAreaParentData {\n        return ((parentData as? QMUITopBarAreaParentData) ?: QMUITopBarAreaParentData()).also {\n            it.area = area\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        val otherModifier = other as? QMUITopBarAreaParentData ?: return false\n        return area == otherModifier.area\n    }\n\n    override fun hashCode(): Int {\n        return area.hashCode()\n    }\n\n    override fun toString(): String =\n        \"QMUITopBarAreaModifier(area=$area)\"\n}"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_checkbox_checked.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n    <path\n        android:pathData=\"M9,0C13.9706,0 18,4.0294 18,9C18,13.9706 13.9706,18 9,18C4.0294,18 0,13.9706 0,9C0,4.0294 4.0294,0 9,0ZM12,6L7.5,10.5L6,9L4.5,10.5L7.5,13.5L13.5,7.5L12,6Z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"#FF00A8E1\"\n        android:fillType=\"evenOdd\"\n        android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_checkbox_normal.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n  <path\n      android:pathData=\"M9,0C13.9706,0 18,4.0294 18,9C18,13.9706 13.9706,18 9,18C4.0294,18 0,13.9706 0,9C0,4.0294 4.0294,0 9,0ZM9,1.5C4.8579,1.5 1.5,4.8579 1.5,9C1.5,13.1421 4.8579,16.5 9,16.5C13.1421,16.5 16.5,13.1421 16.5,9C16.5,4.8579 13.1421,1.5 9,1.5Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#CCCCCC\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_checkbox_partial.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n    <path\n        android:fillColor=\"#FF00A8E1\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M9,18C13.9706,18 18,13.9706 18,9C18,4.0294 13.9706,0 9,0C4.0294,0 0,4.0294 0,9C0,13.9706 4.0294,18 9,18ZM4,8L14,8L14,10L4,10L4,8Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_chevron.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"6dp\"\n    android:height=\"9dp\"\n    android:viewportWidth=\"6\"\n    android:viewportHeight=\"9\">\n  <path\n      android:pathData=\"M3.11747,4.5l-3.11747,3.5109l1.14712,0.9891l3.99574,-4.5l-3.99574,-4.5l-1.14712,0.9891z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#666666\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_mark.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"12dp\"\n    android:height=\"12dp\"\n    android:viewportWidth=\"12\"\n    android:viewportHeight=\"12\">\n  <path\n      android:pathData=\"M0,7l1.5,-1.5l2.5,2.5l6,-6l1.5,1.5l-7.5,7.5z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FF00A8E1\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/drawable/ic_qmui_topbar_back.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"24dp\"\n        android:height=\"24dp\"\n        android:viewportWidth=\"24.0\"\n        android:viewportHeight=\"24.0\">\n    <path\n        android:pathData=\"M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z\"\n        android:fillColor=\"#ffffff\"/>\n</vector>\n"
  },
  {
    "path": "compose-core/src/main/res/values/qmui_ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"qmui_window_inset_cache\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "compose-core/src/test/java/com/qmuiteam/compose/ExampleUnitTest.kt",
    "content": "package com.qmuiteam.compose\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n}"
  },
  {
    "path": "deploy.sh",
    "content": "#!/usr/bin/env bash\n\n#./deploy.sh qmui publishToMavenLocal\n#./deploy.sh arch publishToMavenLocal\n#./deploy.sh type publishToMavenLocal\n#./deploy.sh compose-core publishToMavenLocal\n#./deploy.sh compose publishToMavenLocal\n#./deploy.sh photo publishToMavenLocal\n\n#./deploy.sh qmui publish\n#./deploy.sh arch publish\n#./deploy.sh type publish\n#./deploy.sh compose-core publish\n#./deploy.sh compose publish\n#./deploy.sh photo publish\n\nif [[ \"qmui\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :qmui:clean :qmui:build qmui:$2\"\n    $buildCmd\nelif [[ \"arch\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :arch:clean :arch:build :arch:$2\"\n    $buildCmd\n    buildCmd=\"./gradlew :arch-annotation:clean :arch-annotation:build :arch-annotation:$2\"\n    $buildCmd\n    buildCmd=\"./gradlew :arch-compiler:clean :arch-compiler:build :arch-compiler:$2\"\n    $buildCmd\nelif [[ \"type\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :type:clean :type:build :type:$2\"\n    $buildCmd\nelif [[ \"compose-core\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :compose-core:clean :compose-core:build :compose-core:$2\"\n    $buildCmd\nelif [[ \"compose\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :compose:clean :compose:build :compose:$2\"\n    $buildCmd\nelif [[ \"photo\" == \"$1\" ]]\nthen\n    buildCmd=\"./gradlew :photo:clean :photo:build :photo:$2\"\n    $buildCmd\n    buildCmd=\"./gradlew :photo-coil:clean :photo-coil:build :photo-coil:$2\"\n    $buildCmd\n    buildCmd=\"./gradlew :photo-glide:clean :photo-glide:build :photo-glide:$2\"\n    $buildCmd\nfi"
  },
  {
    "path": "editor/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "editor/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    kotlin(\"kapt\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.editorVer\n\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildFeatures {\n        compose = true\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n}\n\ndependencies {\n    implementation(project(\":compose-core\"))\n}"
  },
  {
    "path": "editor/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "editor/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "editor/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.qmuiteam.editor.editor\"/>\n"
  },
  {
    "path": "editor/src/main/java/com/qmuiteam/editor/EditorBehavior.kt",
    "content": "package com.qmuiteam.editor\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.sp\n\ninterface EditorBehavior {\n    fun apply(value: TextFieldValue): TextFieldValue\n}\n\ninternal fun String.isHeaderTag(): Boolean{\n    return HeaderLevel.values().find { it.tag == this } != null\n}\n\ninternal fun String.isBoldTag(): Boolean{\n    return startsWith(BoldBehavior.prefix)\n}\n\nclass BoldBehavior(val weight: Int = 700) : EditorBehavior {\n\n    companion object {\n        val prefix = \"blod\"\n    }\n\n    val tag: String = \"$prefix:$weight\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.bold(this)\n    }\n}\n\nclass StopBehavior(val target: String): EditorBehavior {\n    companion object {\n        val prefix = \"stop\"\n    }\n\n    val tag: String = \"${prefix}:$target\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value\n    }\n}\n\nclass TextColorBehavior(val color: Color = Color.White) : EditorBehavior {\n\n    companion object {\n        val prefix = \"color\"\n    }\n\n    val tag: String = \"$prefix:$color\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.textColor(this)\n    }\n}\n\nobject NormalParagraphBehavior: EditorBehavior {\n\n    const val tag = \"p\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.quote()\n    }\n}\n\nobject QuoteBehavior : EditorBehavior {\n\n    const val tag = \"quote\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.quote()\n    }\n}\n\n\nobject UnOrderListBehavior : EditorBehavior {\n\n    const val tag = \"ul\"\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.unOrder()\n    }\n}\n\nclass HeaderBehavior(val level: HeaderLevel): EditorBehavior {\n\n    override fun apply(value: TextFieldValue): TextFieldValue {\n        return value.header(level)\n    }\n}\n\nenum class HeaderLevel(val tag: String, val fontSize: TextUnit) {\n    h1(\"h1\", 24.sp),\n    h2(\"h2\", 22.sp),\n    h3(\"h3\", 20.sp),\n    h4(\"h4\", 18.sp),\n    h5(\"h5\", 16.sp)\n}\n"
  },
  {
    "path": "editor/src/main/java/com/qmuiteam/editor/QMUIEditor.kt",
    "content": "package com.qmuiteam.editor\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.rememberScrollState\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.text.BasicTextField\nimport androidx.compose.foundation.verticalScroll\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.focus.FocusRequester\nimport androidx.compose.ui.focus.focusRequester\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.Brush\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.SolidColor\nimport androidx.compose.ui.layout.onPlaced\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.ParagraphStyle\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.unit.DpRect\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.height\nimport androidx.compose.ui.unit.width\nimport com.qmuiteam.compose.core.ui.qmuiPrimaryColor\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\n\n\ninterface EditorDecoration {\n    @Composable\n    fun Compose()\n}\n\nclass QuoteDecoration(val rect: Rect) : EditorDecoration {\n    @Composable\n    override fun Compose() {\n        key(this) {\n            val dpRect = with(LocalDensity.current) {\n                DpRect(rect.left.toDp(), rect.top.toDp(), rect.right.toDp(), rect.bottom.toDp())\n            }\n            Box(\n                Modifier\n                    .offset(dpRect.left, dpRect.top - 6.dp)\n                    .width(dpRect.width)\n                    .height(dpRect.height + 12.dp)\n                    .background(Color.LightGray)\n            ) {\n                Box(\n                    modifier = Modifier\n                        .width(2.dp)\n                        .fillMaxHeight()\n                        .background(Color.Gray)\n                )\n            }\n        }\n    }\n}\n\n\nclass UnOrderedDecoration(val rect: Rect) : EditorDecoration {\n    @Composable\n    override fun Compose() {\n        key(this) {\n            val dpRect = with(LocalDensity.current) {\n                DpRect(rect.left.toDp(), rect.top.toDp(), rect.right.toDp(), rect.bottom.toDp())\n            }\n            Box(\n                Modifier\n                    .offset(dpRect.left, dpRect.top + dpRect.height / 2 - 2.dp)\n                    .width(4.dp)\n                    .height(4.dp)\n                    .clip(CircleShape)\n                    .background(Color.Black)\n            )\n        }\n    }\n}\n\n\n@Composable\nfun QMUIEditor(\n    modifier: Modifier = Modifier,\n    value: TextFieldValue,\n    channel: Channel<EditorBehavior>,\n    hint: AnnotatedString = AnnotatedString(\"\"),\n    hintStyle: TextStyle = TextStyle.Default.copy(color = Color.Gray),\n    textStyle: TextStyle = TextStyle.Default,\n    focusRequester: FocusRequester = remember {\n        FocusRequester()\n    },\n    cursorBrush: Brush = SolidColor(qmuiPrimaryColor),\n    onValueChange: (TextFieldValue) -> Unit\n) {\n\n    var textFieldValue by remember(value) {\n        mutableStateOf(value.check())\n    }\n\n    var editorDecorations by remember {\n        mutableStateOf(listOf<EditorDecoration>())\n    }\n\n    LaunchedEffect(key1 = value) {\n        launch {\n            while (isActive) {\n                val behavior = channel.receive()\n                textFieldValue = behavior.apply(textFieldValue)\n            }\n        }\n    }\n\n    // TODO Fix here, BasicTextField can scroll inner , but i can't read the scroll position.\n    BoxWithConstraints(modifier) {\n        val scrollState = rememberScrollState()\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .verticalScroll(scrollState)\n        ) {\n            BasicTextField(\n                value = textFieldValue,\n                onTextLayout = {\n                    val list = mutableListOf<EditorDecoration>()\n                    it.layoutInput.text.paragraphStyles.forEach { paragraph ->\n                        val rect = if (paragraph.start == paragraph.end) {\n                            val cursorRect = it.multiParagraph.getCursorRect(paragraph.start)\n                            Rect(\n                                0f,\n                                cursorRect.top,\n                                it.multiParagraph.width,\n                                cursorRect.bottom\n                            )\n                        } else {\n                            val start = it.multiParagraph.getBoundingBox(paragraph.start)\n                            val end = it.multiParagraph.getBoundingBox(paragraph.end - 1)\n                            Rect(\n                                0f,\n                                start.top,\n                                it.multiParagraph.width,\n                                end.bottom\n                            )\n                        }\n                        if (paragraph.tag == QuoteBehavior.tag) {\n                            list.add(QuoteDecoration(rect))\n                        } else if (paragraph.tag == UnOrderListBehavior.tag) {\n                            list.add(UnOrderedDecoration(rect))\n                        }\n                    }\n                    editorDecorations = list\n                },\n                onValueChange = {\n                    textFieldValue = updateTextFieldValue(textFieldValue, it)\n                    onValueChange(textFieldValue)\n                },\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .defaultMinSize(minHeight = this@BoxWithConstraints.maxHeight)\n                    .focusRequester(focusRequester),\n                textStyle = textStyle,\n                cursorBrush = cursorBrush,\n                decorationBox = { innerTextField ->\n                    Box(modifier = Modifier.fillMaxSize()) {\n                        editorDecorations.forEach {\n                            it.Compose()\n                        }\n                    }\n                    if (textFieldValue.text.isEmpty()) {\n                        Text(text = hint, style = hintStyle)\n                    }\n                    innerTextField()\n                }\n            )\n        }\n    }\n}\n\nprivate fun updateTextFieldValue(\n    current: TextFieldValue,\n    next: TextFieldValue\n): TextFieldValue {\n    if (current.text == next.text) {\n        return TextFieldValue(current.annotatedString, next.selection, next.composition)\n    }\n    if (next.text.isBlank()) {\n        return TextFieldValue(AnnotatedString(\"\"), next.selection, next.composition).check()\n    }\n\n    val mutableSpan = mutableListOf<MutableRange<SpanStyle>>()\n    val mutableParagraph = mutableListOf<MutableRange<ParagraphStyle>>()\n    current.annotatedString.spanStyles.forEach {\n        mutableSpan.add(MutableRange(it.item, it.start, it.end, it.tag))\n    }\n    current.annotatedString.paragraphStyles.forEach {\n        mutableParagraph.add(MutableRange(it.item, it.start, it.end, it.tag))\n    }\n    var indexCorrect = 0\n    wordEdit(current, next).list.forEach { point ->\n        val lastIndex = point.oldIndex + indexCorrect\n        if (point.action == WordEditAction.insert) {\n            val toInsertPos = lastIndex + 1\n            mutableParagraph.forEachIndexed { index, item ->\n                item.modifyByInsert(toInsertPos, index == mutableParagraph.size - 1)\n            }\n            val stopSpans = mutableListOf<MutableRange<SpanStyle>>()\n            val normalSpans = mutableListOf<MutableRange<SpanStyle>>()\n            mutableSpan.forEach {\n                if (it.tag.startsWith(StopBehavior.prefix)) {\n                    stopSpans.add(it)\n                } else {\n                    normalSpans.add(it)\n                }\n            }\n            mutableSpan.forEach { item ->\n                item.modifyByInsert(\n                    toInsertPos,\n                    stopSpans.find { it.end == item.end && it.tag.endsWith(item.tag) } == null\n                )\n                // update companion span.\n                mutableParagraph.find { it.tag == item.tag && it.start == item.start }?.let {\n                    item.end = it.end\n                }\n            }\n            stopSpans.forEach {\n                it.modifyByInsert(toInsertPos, true)\n                if (it.end > it.start) {\n                    mutableSpan.remove(it)\n                }\n            }\n\n            if (next.text[point.newIndex] == '\\n') {\n                for (i in 0 until mutableParagraph.size) {\n                    val paragraph = mutableParagraph[i]\n                    if (paragraph.start <= point.newIndex && paragraph.end > point.newIndex) {\n                        if (!paragraph.tag.isHeaderTag()) {\n                            mutableParagraph.add(i + 1, MutableRange(paragraph.item, point.newIndex + 1, paragraph.end, paragraph.tag))\n                        } else {\n                            mutableParagraph.add(i + 1, MutableRange(ParagraphStyle(), point.newIndex + 1, paragraph.end, \"p\"))\n                            mutableSpan.find {\n                                it.start == paragraph.start && it.end == paragraph.end && it.tag == \"h\"\n                            }?.let { it.end = point.newIndex + 1 }\n                        }\n                        paragraph.end = point.newIndex + 1\n                        break\n                    }\n                }\n            }\n            indexCorrect++\n        } else if (point.action == WordEditAction.delete) {\n\n            if (current.text[point.oldIndex] == '\\n') {\n                val prevParagraph = mutableParagraph.find { it.end == point.oldIndex + 1 }\n                val nextParagraph = mutableParagraph.find { it.start == point.oldIndex + 1 && it.end != it.start }\n                nextParagraph?.let { np ->\n                    prevParagraph?.let { pp ->\n                        pp.end = np.end\n                        mutableSpan.find { it.start == pp.start && it.tag == pp.tag }?.let {\n                            it.end = np.end\n                        }\n                    }\n                    mutableParagraph.remove(np)\n                    mutableSpan.removeAll { np.start == it.start && it.tag == np.tag }\n                }\n            }\n\n            var i = 0\n            while (i < mutableSpan.size) {\n                val span = mutableSpan[i]\n                val shouldRemove = span.modifyByDelete(lastIndex)\n                if (shouldRemove) {\n                    mutableSpan.removeAt(i)\n                    i -= 1\n                }\n                i++\n            }\n            i = 0\n\n            while (i < mutableParagraph.size) {\n                val paragraph = mutableParagraph[i]\n                val shouldRemove = paragraph.modifyByDelete(lastIndex)\n                if (shouldRemove) {\n                    mutableParagraph.removeAt(i)\n                    i -= 1\n                }\n                i++\n            }\n\n            indexCorrect--\n        }\n    }\n    mutableSpan.removeAll {\n        it.start == it.end && (it.end < next.selection.start || it.start > next.selection.end)\n    }\n    mutableParagraph.removeAll {\n        it.start == it.end && (it.end < next.selection.start || it.start > next.selection.end)\n    }\n    val spanStyles = mutableSpan.map {\n        AnnotatedString.Range(it.item, it.start, it.end, it.tag)\n    }\n\n    val paragraphStyles = mutableParagraph.map {\n        AnnotatedString.Range(it.item, it.start, it.end, it.tag)\n    }\n    return TextFieldValue(\n        AnnotatedString(next.text, spanStyles, paragraphStyles),\n        next.selection,\n        next.composition\n    )\n}"
  },
  {
    "path": "editor/src/main/java/com/qmuiteam/editor/Range.kt",
    "content": "package com.qmuiteam.editor\n\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.TextRange\n\ninternal class MutableRange<T>(\n    var item: T,\n    var start: Int,\n    var end: Int,\n    var tag: String\n) {\n\n    fun modifyByInsert(insertPos: Int, appendIfAtEnd: Boolean) {\n        if (start == end) {\n            if (insertPos < start) {\n                start++\n                end++\n            } else if (insertPos == start) {\n                end++\n            }\n        } else {\n            if (insertPos < start) {\n                start++\n                end++\n            } else if (insertPos < end || (appendIfAtEnd && insertPos == end)) {\n                end++\n            }\n        }\n    }\n\n    fun modifyByDelete(deletePos: Int): Boolean {\n        if (start == end) {\n            if (deletePos < start - 1) {\n                start--\n                end--\n            } else if (deletePos == start - 1) {\n                start--\n                end--\n                return true\n            }\n        }\n        if (deletePos < start) {\n            start--\n            end--\n        } else if (deletePos < end) {\n            end--\n        }\n        return false\n    }\n\n    fun isCursorContained(cursorPos: Int): Boolean{\n        return if(start == end){\n            start == cursorPos\n        } else {\n            cursorPos in (start + 1) until end\n        }\n    }\n}\n\nfun <T> AnnotatedString.Range<T>.isCursorContained(cursorPos: Int): Boolean{\n    return if(start == end){\n        start == cursorPos\n    } else {\n        cursorPos in (start + 1)..end\n    }\n}"
  },
  {
    "path": "editor/src/main/java/com/qmuiteam/editor/TextFieldValueEx.kt",
    "content": "package com.qmuiteam.editor\n\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.ParagraphStyle\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.text.style.TextIndent\nimport androidx.compose.ui.unit.sp\n\n//region ==============  spanStyle ==============\nfun TextFieldValue.bold(bold: BoldBehavior): TextFieldValue {\n    return textStyle(\n        style = SpanStyle(fontWeight = FontWeight(bold.weight)),\n        tag = bold.tag\n    ) {\n        it.isBoldTag()\n    }\n}\n\nfun TextFieldValue.textColor(textColor: TextColorBehavior): TextFieldValue {\n    return textStyle(\n        style = SpanStyle(color = textColor.color),\n        tag = textColor.tag\n    ) {\n        it.isBoldTag()\n    }\n}\n\nprivate fun TextFieldValue.textStyle(style: SpanStyle, tag: String, shouldHandle: (String) -> Boolean): TextFieldValue {\n    return modifySpans { spans ->\n        if (selection.collapsed) {\n            val contained = spans.find {\n                it.tag.isBoldTag() && it.isCursorContained(selection.start)\n            }\n            if (contained == null) {\n                spans.add(MutableRange(style, selection.start, selection.end, tag))\n            }\n        } else {\n            var i = 0\n            var handled = false\n            while (i < spans.size) {\n                val span = spans[i]\n                if (shouldHandle(span.tag)) {\n                    if (span.start >= selection.start && span.end <= selection.end) {\n                        spans.removeAt(i)\n                        i--\n                    } else if (span.end > selection.end && span.start < selection.start) {\n                        if (span.tag == tag) {\n                            handled = true\n                            break\n                        }\n                        spans.add(i, MutableRange(span.item, selection.end, span.end, span.tag))\n                        span.end = selection.start\n                        i++\n                    } else if (span.end > selection.start && span.start < selection.end) {\n                        if (span.start >= selection.start) {\n                            span.start = selection.end\n                        }\n                        if (span.end <= selection.end) {\n                            span.end = selection.start\n                        }\n                    }\n                }\n                i++\n            }\n\n            if (!handled) {\n                spans.add(MutableRange(style, selection.start, selection.end, tag))\n            }\n        }\n    }\n}\n\nprivate fun TextFieldValue.modifySpans(\n    block: (spans: MutableList<MutableRange<SpanStyle>>) -> Unit\n): TextFieldValue {\n    val mutableSpans = mutableListOf<MutableRange<SpanStyle>>()\n    annotatedString.spanStyles.forEach {\n        mutableSpans.add(MutableRange(it.item, it.start, it.end, it.tag))\n    }\n    block(mutableSpans)\n    val spanStyles = mutableSpans.map {\n        AnnotatedString.Range(it.item, it.start, it.end, it.tag)\n    }\n\n    return TextFieldValue(\n        AnnotatedString(text, spanStyles, annotatedString.paragraphStyles),\n        selection,\n        composition\n    )\n}\n\n//endregion\n\n//region ==============  paragraphStyle ==============\n\ninternal fun TextFieldValue.quote(): TextFieldValue {\n    return paragraphStyle(\n        ParagraphStyle(\n            textIndent = TextIndent(10.sp, 10.sp)\n        ),\n        QuoteBehavior.tag\n    )\n}\n\ninternal fun TextFieldValue.unOrder(): TextFieldValue {\n    return paragraphStyle(\n        ParagraphStyle(\n            textIndent = TextIndent(10.sp, 10.sp)\n        ),\n        UnOrderListBehavior.tag\n    )\n}\n\ninternal fun TextFieldValue.header(level: HeaderLevel): TextFieldValue {\n    return paragraphStyle(\n        ParagraphStyle(),\n        level.tag,\n        SpanStyle(fontSize = level.fontSize)\n    )\n}\n\nprivate fun MutableRange<ParagraphStyle>.replaceStyleIfNeeded(\n    value: TextFieldValue,\n    tag: String,\n    style: ParagraphStyle\n): AnnotatedString.Range<ParagraphStyle>? {\n    if (value.selection.collapsed) {\n        val shouldModify = when {\n            start == end -> start == value.selection.start\n            value.selection.start in start until end -> true\n            value.selection.start == end -> value.text[end - 1] != '\\n'\n            else -> false\n        }\n        if (shouldModify) {\n            if (this.tag != tag) {\n                val ret = AnnotatedString.Range(item, start, end)\n                this.item = style\n                this.tag = tag\n                return ret\n            }\n        }\n    } else {\n        if (start < value.selection.end && end > value.selection.start && this.tag != tag) {\n            val ret = AnnotatedString.Range(item, start, end)\n            this.item = style\n            this.tag = tag\n            return ret\n        }\n    }\n    return null\n}\n\nprivate fun TextFieldValue.paragraphStyle(\n    style: ParagraphStyle,\n    tag: String,\n    companionSpan: SpanStyle? = null\n): TextFieldValue {\n    val replacedParagraphs = mutableListOf<AnnotatedString.Range<ParagraphStyle>>()\n    val paragraphs = modifyParagraphs { paragraphs ->\n        paragraphs.forEach { paragraph ->\n            paragraph.replaceStyleIfNeeded(this, tag, style)?.let {\n                replacedParagraphs.add(it)\n            }\n        }\n    }\n\n    if (replacedParagraphs.isEmpty()) {\n        return paragraphs\n    }\n\n    return paragraphs.modifySpans { spans ->\n        spans.removeAll { span ->\n            replacedParagraphs.find {\n                it.start == span.start && it.end == span.end && it.tag == span.tag\n            } != null\n        }\n        replacedParagraphs.forEach { range ->\n            companionSpan?.let {\n                spans.add(MutableRange(it, range.start, range.end, tag))\n            }\n        }\n\n    }\n}\n\n\nprivate fun TextFieldValue.modifyParagraphs(\n    block: (paragraphs: MutableList<MutableRange<ParagraphStyle>>) -> Unit\n): TextFieldValue {\n    val mutableParagraphs = mutableListOf<MutableRange<ParagraphStyle>>()\n    annotatedString.paragraphStyles.forEach {\n        mutableParagraphs.add(MutableRange(it.item, it.start, it.end, it.tag))\n    }\n\n    block(mutableParagraphs)\n\n    val paragraphStyles = mutableParagraphs.map {\n        AnnotatedString.Range(it.item, it.start, it.end, it.tag)\n    }\n\n    return TextFieldValue(\n        AnnotatedString(text, annotatedString.spanStyles, paragraphStyles),\n        selection,\n        composition\n    )\n}\n\n//endregion\n\ninternal fun TextFieldValue.check(): TextFieldValue {\n    val paragraphs = mutableListOf<AnnotatedString.Range<ParagraphStyle>>()\n    var currentIndex = 0\n    var nextIndex = text.indexOf('\\n')\n    while (nextIndex >= 0) {\n        val exist = annotatedString.paragraphStyles.find { it.start == currentIndex && it.end == nextIndex + 1 }\n        if (exist == null) {\n            paragraphs.add(AnnotatedString.Range(ParagraphStyle(), currentIndex, nextIndex + 1, NormalParagraphBehavior.tag))\n        } else {\n            paragraphs.add(exist)\n        }\n        currentIndex = nextIndex + 1\n        nextIndex = text.indexOf('\\n', currentIndex)\n    }\n\n    if (currentIndex < text.length) {\n        val exist = annotatedString.paragraphStyles.find { it.start == currentIndex && it.end == text.length }\n        if (exist == null) {\n            paragraphs.add(AnnotatedString.Range(ParagraphStyle(), currentIndex, text.length, NormalParagraphBehavior.tag))\n        } else {\n            paragraphs.add(exist)\n        }\n    }\n\n    if (text.isEmpty() || (selection.collapsed && selection.end == text.length)) {\n        val exist = annotatedString.paragraphStyles.find { it.start == text.length && it.end == text.length }\n        if (exist == null) {\n            paragraphs.add(AnnotatedString.Range(ParagraphStyle(), text.length, text.length, NormalParagraphBehavior.tag))\n        } else {\n            paragraphs.add(exist)\n        }\n    }\n    return TextFieldValue(\n        AnnotatedString(text, annotatedString.spanStyles, paragraphs),\n        selection,\n        composition\n    )\n}"
  },
  {
    "path": "editor/src/main/java/com/qmuiteam/editor/WordEdit.kt",
    "content": "package com.qmuiteam.editor\n\nimport androidx.compose.ui.text.input.TextFieldValue\nimport java.util.*\n\nenum class WordEditAction {\n    insert, delete, repace\n}\n\n\ndata class WordEditPoint(\n    val action: WordEditAction,\n    val oldIndex: Int,\n    val newIndex: Int\n)\n\nclass WordEditResult(\n    val dis: Int,\n    val list: List<WordEditPoint>\n)\n\nprivate class WordEditRecordNode(val point: WordEditPoint) {\n\n    var prev: WordEditRecordNode? = null\n}\n\nprivate class WordEditRecord(\n    var dis: Int\n) {\n    var node: WordEditRecordNode? = null\n}\n\nfun wordEdit(oldTextFieldValue: TextFieldValue, newTextFieldValue: TextFieldValue): WordEditResult {\n    val oldText = oldTextFieldValue.text\n    val newText = newTextFieldValue.text\n    if(oldText.length <= 20 || newText.length <= 20){\n        return wordEdit(oldText, newText)\n    }\n\n    var prefixCheckLength = 10\n    var prefix = (oldTextFieldValue.selection.start - prefixCheckLength)\n        .coerceAtMost(newTextFieldValue.selection.start - prefixCheckLength)\n        .coerceAtLeast(0)\n    while (prefix > 0){\n        if(oldText.substring(0, prefix) == newText.substring(0, prefix)){\n            break\n        }\n        prefixCheckLength *= 2\n        prefix = (prefix - prefixCheckLength).coerceAtLeast(0)\n    }\n\n    var suffixCheckLength = 10\n    var suffix = (oldText.length - oldTextFieldValue.selection.end - suffixCheckLength)\n        .coerceAtMost(newText.length - newTextFieldValue.selection.end - suffixCheckLength)\n        .coerceAtLeast(0)\n    while (suffix > 0){\n        if(oldText.substring(oldText.length - suffix) == newText.substring(newText.length - suffix)){\n            break\n        }\n        suffixCheckLength *= 2\n        suffix = (suffix - suffixCheckLength).coerceAtLeast(0)\n    }\n    if(prefix == 0 && suffix == 0){\n        return wordEdit(oldText, newText)\n    }\n    return wordEdit(\n        oldText.substring(prefix, oldText.length - suffix),\n        newText.substring(prefix, newText.length - suffix)\n    )\n}\n\nfun wordEdit(oldText: String, newText: String, shift: Int = 0): WordEditResult {\n    val array = arrayOfNulls<WordEditRecord>(oldText.length + 1)\n    val next = arrayOfNulls<WordEditRecord>(oldText.length + 1)\n    for (j in array.indices) {\n        array[j] = WordEditRecord(j).apply {\n            if (j > 0) {\n                node = WordEditRecordNode(\n                    WordEditPoint(\n                        WordEditAction.delete,\n                        shift + j - 1,\n                        -1\n                    )\n                ).apply {\n                    prev = array[j - 1]!!.node\n                }\n            }\n        }\n    }\n    for (i in newText.indices) {\n        for (j in array.indices) {\n            val columnLast = array[j]!!\n            if (j == 0) {\n                next[j] = WordEditRecord(columnLast.dis + 1).apply {\n                    node = WordEditRecordNode(\n                        WordEditPoint(WordEditAction.insert, shift + j - 1, shift + i)\n                    ).apply {\n                        prev = columnLast.node\n                    }\n                }\n            } else {\n                val path1 = WordEditRecord(columnLast.dis + 1).apply {\n                    node = WordEditRecordNode(\n                        WordEditPoint(WordEditAction.insert, shift + j - 1, shift + i)\n                    ).apply {\n                        prev = columnLast.node\n                    }\n                }\n\n                val rowLast = next[j - 1]!!\n                val path2 = WordEditRecord(rowLast.dis + 1).apply {\n                    node = WordEditRecordNode(\n                        WordEditPoint(WordEditAction.delete, shift + j - 1, -1)\n                    ).apply {\n                        prev = rowLast.node\n                    }\n                }\n\n                val diagonalLast = array[j - 1]!!\n                val path3 = if (newText[i] == oldText[j - 1]) {\n                    diagonalLast\n                } else {\n                    WordEditRecord(diagonalLast.dis + 1).apply {\n                        node = WordEditRecordNode(\n                            WordEditPoint(\n                                WordEditAction.repace,\n                                j - 1,\n                                i\n                            )\n                        ).apply {\n                            prev = diagonalLast.node\n                        }\n                    }\n                }\n\n                var minPath = path1\n                if (path2.dis < minPath.dis) {\n                    minPath = path2\n                }\n\n                if (path3.dis < minPath.dis) {\n                    minPath = path3\n                }\n                next[j] = minPath\n            }\n        }\n        for (j in array.indices) {\n            array[j] = next[j]\n        }\n    }\n    val ret = array[array.size - 1]!!\n    val list = LinkedList<WordEditPoint>()\n    var node = ret.node\n    while (node != null) {\n        list.addFirst(node!!.point)\n        node = node?.prev\n    }\n    return WordEditResult(ret.dis, list)\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Jun 11 14:18:59 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4.2-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# suppress inspection \"UnusedProperty\" for whole file\n# 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.\norg.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC\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\nandroid.injected.testOnly=false\nandroid.useAndroidX=true\nandroid.disableAutomaticComponentCreation=true\nandroid.defaults.buildfeatures.buildconfig=false\nandroid.defaults.buildfeatures.aidl=false\nandroid.defaults.buildfeatures.shaders=false\n\nGROUP=com.qmuiteam\nQMUI_VERSION=2.0.1\nQMUI_ARCH_VERSION=2.0.1\nQMUI_TYPE_VERSION = 0.0.14\nPOM_GIT_URL=https://github.com/Tencent/QMUI_Android/\nPOM_SITE_URL=https://qmuiteam.com/android"
  },
  {
    "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\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\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%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "lib/.gitignore",
    "content": "/build\n/*.iml"
  },
  {
    "path": "lib/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    `java-library`\n}\n\njava {\n    sourceCompatibility = Dep.javaVersion\n    targetCompatibility = Dep.javaVersion\n}\n"
  },
  {
    "path": "lib/src/main/java/com/qmuiteam/qmuidemo/lib/Group.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.lib;\n\n/**\n * @author cginechen\n * @date 2016-12-14\n */\n\npublic enum Group {\n    Component,\n    Helper,\n    Lab,\n    Other\n}\n"
  },
  {
    "path": "lib/src/main/java/com/qmuiteam/qmuidemo/lib/annotation/Widget.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.lib.annotation;\n\nimport com.qmuiteam.qmuidemo.lib.Group;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.CLASS)\n@Target(ElementType.TYPE)\npublic @interface Widget {\n    Group group() default Group.Component;\n\n    Class widgetClass() default void.class;\n\n    String name() default \"\";\n\n    String docUrl() default \"\";\n\n    int iconRes() default 0;\n}\n"
  },
  {
    "path": "photo/.gitignore",
    "content": "/build"
  },
  {
    "path": "photo/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.photoVer\n\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildFeatures {\n        compose = true\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n}\n\ndependencies {\n    implementation(Dep.AndroidX.appcompat)\n    api(project(\":compose-core\"))\n    implementation(Dep.AndroidX.activity)\n    implementation(Dep.Compose.activity)\n    implementation(Dep.Compose.pager)\n    implementation(Dep.Compose.constraintlayout)\n}"
  },
  {
    "path": "photo/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "photo/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "photo/src/androidTest/java/com/qmuiteam/ExampleInstrumentedTest.kt",
    "content": "package com.qmuiteam\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.qmuiteam.test\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "photo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qmuiteam.photo\">\n\n</manifest>"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/activity/QMUIPhotoClipActivity.kt",
    "content": "package com.qmuiteam.photo.activity\n\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.core.view.WindowCompat\nimport androidx.lifecycle.lifecycleScope\nimport com.qmuiteam.compose.core.provider.QMUIWindowInsetsProvider\nimport com.qmuiteam.photo.compose.QMUIPhotoClipper\nimport com.qmuiteam.photo.data.QMUIPhotoProvider\nimport com.qmuiteam.photo.util.saveToLocal\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\ninternal const val QMUI_PHOTO_CLIP_URI = \"qmui_photo_clip_uri\"\ninternal const val QMUI_PHOTO_CLIP_SOURCE_RATIO = \"qmui_photo_clip_source_ratio\"\n\nfun Intent.getQMUIPhotoClipResult(): Uri? {\n    return getParcelableExtra(QMUI_PHOTO_CLIP_URI)\n}\n\nabstract class QMUIPhotoClipActivity : AppCompatActivity() {\n\n    companion object {\n        fun intentOf(\n            activity: ComponentActivity,\n            cls: Class<out QMUIPhotoClipActivity>,\n            sourceUri: Uri,\n            sourceRatio: Float = -1f\n        ): Intent {\n            val intent = Intent(activity, cls)\n\n            intent.putExtra(QMUI_PHOTO_CLIP_URI, sourceUri)\n            intent.putExtra(QMUI_PHOTO_CLIP_SOURCE_RATIO, sourceRatio)\n            return intent\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n        WindowCompat.getInsetsController(window, window.decorView)?.let {\n            it.isAppearanceLightNavigationBars = false\n        }\n        window.statusBarColor = android.graphics.Color.TRANSPARENT\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {\n            window.navigationBarColor = android.graphics.Color.TRANSPARENT\n            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {\n                window.navigationBarDividerColor = android.graphics.Color.TRANSPARENT\n                window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES\n            }\n        }\n        val uri = intent.getParcelableExtra<Uri>(QMUI_PHOTO_CLIP_URI)\n        if (uri == null) {\n            finish()\n            return\n        }\n        val ratio = intent.getFloatExtra(QMUI_PHOTO_CLIP_SOURCE_RATIO, -1f)\n        setContent {\n            PageContent(uri, ratio)\n        }\n    }\n\n    @Composable\n    protected abstract fun photoProvider(uri: Uri, ratio: Float): QMUIPhotoProvider\n\n    @Composable\n    protected open fun PageContent(uri: Uri, ratio: Float) {\n        Box(modifier = Modifier.background(Color.Black)) {\n            QMUIWindowInsetsProvider {\n                QMUIPhotoClipper(\n                    photoProvider = photoProvider(uri, ratio)\n                ) { doClip ->\n                    Row(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .align(Alignment.BottomCenter)\n                    ) {\n                        Box(modifier = Modifier\n                            .weight(1f)\n                            .clickable {\n                                finish()\n                            }\n                            .padding(vertical = 16.dp),\n                            contentAlignment = Alignment.Center\n                        ) {\n                            Text(\n                                \"取消\",\n                                fontSize = 20.sp,\n                                color = Color.White,\n                                fontWeight = FontWeight.Bold\n                            )\n                        }\n                        Box(modifier = Modifier\n                            .weight(1f)\n                            .clickable {\n                                doClip()?.let {\n                                    handleResult(it)\n                                }\n                            }\n                            .padding(vertical = 16.dp),\n                            contentAlignment = Alignment.Center\n                        ) {\n                            Text(\n                                \"确定\",\n                                fontSize = 20.sp,\n                                color = Color.White,\n                                fontWeight = FontWeight.Bold\n                            )\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    protected open fun handleResult(bitmap: Bitmap) {\n        lifecycleScope.launch {\n            val ret = kotlin.runCatching {\n                withContext(Dispatchers.IO) {\n                    bitmap.saveToLocal(cacheDir)\n                }\n            }.getOrNull()\n            setResult(RESULT_OK, Intent().apply {\n                putExtra(QMUI_PHOTO_CLIP_URI, ret)\n            })\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/activity/QMUIPhotoPickerActivity.kt",
    "content": "package com.qmuiteam.photo.activity\n\nimport android.Manifest\nimport android.app.Application\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.util.Log\nimport android.view.WindowManager\nimport androidx.activity.ComponentActivity\nimport androidx.activity.OnBackPressedCallback\nimport androidx.activity.compose.setContent\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.activity.viewModels\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.compose.animation.*\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.compose.ui.zIndex\nimport androidx.constraintlayout.compose.ConstraintLayout\nimport androidx.constraintlayout.compose.Dimension\nimport androidx.core.view.WindowCompat\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.lifecycle.AbstractSavedStateViewModelFactory\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport com.google.accompanist.pager.ExperimentalPagerApi\nimport com.google.accompanist.pager.PagerState\nimport com.qmuiteam.compose.core.helper.QMUIGlobal\nimport com.qmuiteam.compose.core.provider.QMUIWindowInsetsProvider\nimport com.qmuiteam.compose.core.ui.QMUITopBar\nimport com.qmuiteam.compose.core.ui.QMUITopBarBackIconItem\nimport com.qmuiteam.compose.core.ui.QMUITopBarItem\nimport com.qmuiteam.compose.core.ui.QMUITopBarWithLazyScrollState\nimport com.qmuiteam.photo.compose.*\nimport com.qmuiteam.photo.compose.picker.*\nimport com.qmuiteam.photo.data.*\nimport com.qmuiteam.photo.vm.*\nimport kotlinx.coroutines.flow.*\nimport kotlinx.coroutines.launch\n\nconst val QMUI_PHOTO_DEFAULT_PICK_LIMIT_COUNT = 9\ninternal const val QMUI_PHOTO_RESULT_URI_LIST = \"qmui_photo_result_uri_list\"\ninternal const val QMUI_PHOTO_RESULT_ORIGIN_OPEN = \"qmui_photo_result_origin_open\"\ninternal const val QMUI_PHOTO_ENABLE_ORIGIN = \"qmui_photo_enable_origin\"\ninternal const val QMUI_PHOTO_PICK_LIMIT_COUNT = \"qmui_photo_pick_limit_count\"\ninternal const val QMUI_PHOTO_PICKED_ITEMS = \"qmui_photo_picked_items\"\ninternal const val QMUI_PHOTO_PROVIDER_FACTORY = \"qmui_photo_provider_factory\"\n\nclass QMUIPhotoPickItemInfo(\n    val id: Long,\n    val name: String,\n    val width: Int,\n    val height: Int,\n    val uri: Uri,\n    val rotation: Int\n) : Parcelable {\n\n    fun ratio(): Float {\n        if(height <= 0 || width <= 0){\n            return -1f\n        }\n        if(rotation == 90 || rotation == 270){\n            return height.toFloat() / width\n        }\n        return width.toFloat() / height\n    }\n\n    constructor(parcel: Parcel) : this(\n        parcel.readLong(),\n        parcel.readString()!!,\n        parcel.readInt(),\n        parcel.readInt(),\n        parcel.readParcelable(Uri::class.java.classLoader)!!,\n        parcel.readInt()\n    )\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeLong(id)\n        dest.writeString(name)\n        dest.writeInt(width)\n        dest.writeInt(height)\n        dest.writeParcelable(uri, flags)\n        dest.writeInt(rotation)\n\n    }\n\n    companion object CREATOR : Parcelable.Creator<QMUIPhotoPickItemInfo> {\n        override fun createFromParcel(parcel: Parcel): QMUIPhotoPickItemInfo {\n            return QMUIPhotoPickItemInfo(parcel)\n        }\n\n        override fun newArray(size: Int): Array<QMUIPhotoPickItemInfo?> {\n            return arrayOfNulls(size)\n        }\n    }\n\n}\n\nclass QMUIPhotoPickResult(val list: List<QMUIPhotoPickItemInfo>, val isOriginOpen: Boolean)\n\nfun Intent.getQMUIPhotoPickResult(): QMUIPhotoPickResult? {\n    val list = getParcelableArrayListExtra<QMUIPhotoPickItemInfo>(QMUI_PHOTO_RESULT_URI_LIST) ?: return null\n    if (list.isEmpty()) {\n        return null\n    }\n    val isOriginOpen = getBooleanExtra(QMUI_PHOTO_RESULT_ORIGIN_OPEN, false)\n    return QMUIPhotoPickResult(list, isOriginOpen)\n}\n\n\nopen class QMUIPhotoPickerActivity : AppCompatActivity() {\n\n    companion object {\n\n        fun intentOf(\n            activity: ComponentActivity,\n            cls: Class<out QMUIPhotoPickerActivity>,\n            factoryCls: Class<out QMUIMediaPhotoProviderFactory>,\n            pickedItems: ArrayList<Uri> = arrayListOf(),\n            pickLimitCount: Int = QMUI_PHOTO_DEFAULT_PICK_LIMIT_COUNT,\n            enableOrigin: Boolean = true\n        ): Intent {\n            val intent = Intent(activity, cls)\n            intent.putExtra(QMUI_PHOTO_PICK_LIMIT_COUNT, pickLimitCount)\n            intent.putParcelableArrayListExtra(QMUI_PHOTO_PICKED_ITEMS, pickedItems)\n            intent.putExtra(QMUI_PHOTO_PROVIDER_FACTORY, factoryCls.name)\n            intent.putExtra(QMUI_PHOTO_ENABLE_ORIGIN, enableOrigin)\n            return intent\n        }\n    }\n\n    private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {\n        onHandlePermissionResult(it)\n    }\n\n    private val viewModel by viewModels<QMUIPhotoPickerViewModel>(factoryProducer = {\n        object : AbstractSavedStateViewModelFactory(this@QMUIPhotoPickerActivity, intent?.extras) {\n            override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {\n                val constructor = modelClass.getDeclaredConstructor(\n                    Application::class.java,\n                    SavedStateHandle::class.java,\n                    QMUIMediaDataProvider::class.java,\n                    Array<String>::class.java\n                )\n                return constructor.newInstance(\n                    this@QMUIPhotoPickerActivity.application,\n                    handle,\n                    dataProvider(),\n                    supportedMimeTypes()\n                )\n            }\n\n        }\n    })\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n        WindowCompat.getInsetsController(window, window.decorView)?.let {\n            it.isAppearanceLightNavigationBars = false\n        }\n        window.statusBarColor = android.graphics.Color.TRANSPARENT\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {\n            window.navigationBarColor = android.graphics.Color.TRANSPARENT\n            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {\n                window.navigationBarDividerColor = android.graphics.Color.TRANSPARENT\n                window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES\n            }\n        }\n        setContent {\n            PageContent(viewModel)\n        }\n        onStartCheckPermission()\n        onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {\n            override fun handleOnBackPressed() {\n                when (viewModel.photoPickerSceneFlow.value) {\n                    is QMUIPhotoPickerEditScene -> {\n                        viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene)\n                    }\n                    is QMUIPhotoPickerPreviewScene -> {\n                        viewModel.updateScene(QMUIPhotoPickerGridScene)\n                    }\n                    else -> {\n                        isEnabled = false\n                        onBackPressed()\n                        isEnabled = true\n                    }\n                }\n            }\n\n        })\n    }\n\n    @Composable\n    protected open fun PageContent(viewModel: QMUIPhotoPickerViewModel) {\n        QMUIDefaultPickerConfigProvider {\n            QMUIWindowInsetsProvider {\n                Box(\n                    modifier = Modifier\n                        .fillMaxSize()\n                        .background(QMUILocalPickerConfig.current.screenBgColor)\n                ) {\n                    PhotoPicker(viewModel)\n                }\n            }\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.PhotoPicker(viewModel: QMUIPhotoPickerViewModel) {\n        val data by viewModel.photoPickerDataFlow.collectAsState()\n        when (data.state) {\n            QMUIPhotoPickerLoadState.dataLoading,\n            QMUIPhotoPickerLoadState.permissionChecking -> {\n                Loading()\n            }\n            QMUIPhotoPickerLoadState.permissionDenied -> {\n                PermissionDenied()\n            }\n            QMUIPhotoPickerLoadState.dataLoaded -> {\n                val error = data.error\n                val list = data.data\n                if (error != null) {\n                    PageError(error)\n                } else if (list == null || list.isEmpty()) {\n                    PageEmpty()\n                } else {\n                    PhotoPickerContent(viewModel, list)\n                }\n            }\n        }\n    }\n\n    @OptIn(ExperimentalAnimationApi::class)\n    @Composable\n    protected open fun BoxScope.PhotoPickerContent(\n        viewModel: QMUIPhotoPickerViewModel,\n        data: List<QMUIMediaPhotoBucketVO>\n    ) {\n        val pickedItems by viewModel.pickedListFlow.collectAsState()\n        val sceneState = viewModel.photoPickerSceneFlow.collectAsState()\n        val scene = sceneState.value\n\n        AnimatedVisibility(\n            visible = scene is QMUIPhotoPickerGridScene,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            PhotoPickerGridScene(viewModel, data, pickedItems)\n        }\n        AnimatedVisibility(\n            visible = scene is QMUIPhotoPickerPreviewScene,\n            enter = if(viewModel.prevScene !is QMUIPhotoPickerEditScene) fadeIn() + scaleIn(initialScale = 0.8f) else fadeIn(initialAlpha = 1f),\n            exit = if(scene !is QMUIPhotoPickerEditScene) fadeOut() + scaleOut(targetScale = 0.8f) else fadeOut(targetAlpha = 1f)\n        ) {\n            // For exit animation\n            val previewSceneHolder = remember {\n                SceneHolder(scene as? QMUIPhotoPickerPreviewScene)\n            }\n            if(scene is QMUIPhotoPickerPreviewScene){\n                previewSceneHolder.scene = scene\n            }\n            val previewScene = previewSceneHolder.scene\n            if (previewScene != null) {\n                PhotoPickerPreviewScene(viewModel, previewScene, data, pickedItems)\n            }\n        }\n        AnimatedVisibility(\n            visible = scene is QMUIPhotoPickerEditScene,\n            enter = fadeIn() + scaleIn(initialScale = 0.8f),\n            exit = fadeOut() + scaleOut(targetScale = 0.8f)\n        ) {\n            val editSceneHolder = remember {\n                SceneHolder(scene as? QMUIPhotoPickerEditScene)\n            }\n            if(scene is QMUIPhotoPickerEditScene){\n                editSceneHolder.scene = scene\n            }\n            val editScene = editSceneHolder.scene\n            if (editScene != null) {\n                PhotoPickerEditScene(viewModel, editScene)\n            }\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.PhotoPickerGridScene(\n        viewModel: QMUIPhotoPickerViewModel,\n        data: List<QMUIMediaPhotoBucketVO>,\n        pickedItems: List<Long>,\n        topBarBackItem: QMUITopBarItem = remember {\n            QMUITopBarBackIconItem {\n                finish()\n            }\n        }\n    ) {\n\n        LaunchedEffect(\"\") {\n            WindowCompat.getInsetsController(window, window.decorView)?.show(WindowInsetsCompat.Type.statusBars())\n        }\n\n        var currentBucket by remember {\n            mutableStateOf(data.first())\n        }\n\n        val scrollState = viewModel.gridSceneScrollState\n\n        val bucketFlow = remember {\n            MutableStateFlow(currentBucket.name)\n        }.apply {\n            value = currentBucket.name\n        }\n\n        val isFocusBucketFlow = remember {\n            MutableStateFlow(false)\n        }\n\n        val config = QMUILocalPickerConfig.current\n        val topBarBucketItem = remember(config) {\n            config.topBarBucketFactory(bucketFlow, isFocusBucketFlow) {\n                isFocusBucketFlow.value = !isFocusBucketFlow.value\n            }\n        }\n\n        val isFocusBucketChooser by isFocusBucketFlow.collectAsState()\n\n        val topBarSendItem = remember(config) {\n            config.topBarSendFactory(false, viewModel.pickLimitCount, viewModel.pickedCountFlow) {\n                onHandleSend(viewModel.getPickedResultList())\n            }\n        }\n\n        val topBarLeftItems = remember(topBarBackItem, topBarBucketItem) {\n            arrayListOf(topBarBackItem, topBarBucketItem)\n        }\n\n        val topBarRightItems = remember(topBarSendItem) {\n            arrayListOf(topBarSendItem)\n        }\n\n        Column(modifier = Modifier.fillMaxSize()) {\n            QMUITopBarWithLazyScrollState(\n                scrollState = scrollState,\n                paddingEnd = 16.dp,\n                separatorHeight = 0.dp,\n                backgroundColor = QMUILocalPickerConfig.current.topBarBgColor,\n                leftItems = topBarLeftItems,\n                rightItems = topBarRightItems\n            )\n            ConstraintLayout(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .weight(1f)\n            ) {\n                val (content, toolbar) = createRefs()\n                QMUIPhotoPickerGrid(\n                    data = currentBucket.list,\n                    modifier = Modifier.constrainAs(content) {\n                        width = Dimension.fillToConstraints\n                        height = Dimension.fillToConstraints\n                        top.linkTo(parent.top)\n                        start.linkTo(parent.start)\n                        end.linkTo(parent.end)\n                        bottom.linkTo(toolbar.top)\n                    },\n                    state = scrollState,\n                    pickedItems = pickedItems,\n                    onPickItem = { _, model ->\n                        viewModel.togglePick(model)\n                    },\n                    onPreview = {\n                        viewModel.updateScene(QMUIPhotoPickerPreviewScene(currentBucket.id, false, it.id))\n                    }\n                )\n                QMUIPhotoPickerGridToolBar(\n                    modifier = Modifier\n                        .constrainAs(toolbar) {\n                            width = Dimension.fillToConstraints\n                            start.linkTo(parent.start)\n                            end.linkTo(parent.end)\n                            bottom.linkTo(parent.bottom)\n                        },\n                    enableOrigin = viewModel.enableOrigin,\n                    pickedItems = pickedItems,\n                    isOriginOpenFlow = viewModel.isOriginOpenFlow,\n                    onToggleOrigin = {\n                        viewModel.toggleOrigin(it)\n                    }\n                ) {\n                    viewModel.updateScene(QMUIPhotoPickerPreviewScene(currentBucket.id, true, currentBucket.list.first().model.id))\n                }\n                QMUIPhotoBucketChooser(\n                    focus = isFocusBucketChooser,\n                    data = data,\n                    currentId = currentBucket.id,\n                    onBucketClick = {\n                        currentBucket = it\n                        isFocusBucketFlow.value = false\n                    }) {\n                    isFocusBucketFlow.value = false\n                }\n            }\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.PhotoPickerPreviewScene(\n        viewModel: QMUIPhotoPickerViewModel,\n        scene: QMUIPhotoPickerPreviewScene,\n        data: List<QMUIMediaPhotoBucketVO>,\n        pickedItems: List<Long>\n    ) {\n        val list = remember(scene) {\n            if (scene.onlySelected) {\n                viewModel.getPickedVOList()\n            } else {\n                data.find { it.id == scene.buckedId }?.list ?: emptyList<QMUIMediaPhotoVO>()\n            }\n        }\n        PhotoPickerPreviewContent(viewModel, list, pickedItems, scene)\n    }\n\n    @OptIn(ExperimentalPagerApi::class)\n    @Composable\n    protected open fun BoxScope.PhotoPickerPreviewContent(\n        viewModel: QMUIPhotoPickerViewModel,\n        data: List<QMUIMediaPhotoVO>,\n        pickedItems: List<Long>,\n        scene: QMUIPhotoPickerPreviewScene\n    ) {\n        val config = QMUILocalPickerConfig.current\n        var isFullPageState by remember {\n            mutableStateOf(false)\n        }\n        LaunchedEffect(isFullPageState) {\n            WindowCompat.getInsetsController(window, window.decorView)?.let {\n                if (!isFullPageState) {\n                    it.show(WindowInsetsCompat.Type.statusBars())\n                } else {\n                    it.hide(WindowInsetsCompat.Type.statusBars())\n                }\n\n            }\n        }\n        val pagerState = remember(data, scene.currentId) {\n            PagerState(\n                currentPage = data.indexOfFirst { it.model.id == scene.currentId }.coerceAtLeast(0),\n            )\n        }\n\n        val topBarLeftItems = remember {\n            arrayListOf<QMUITopBarItem>(QMUITopBarBackIconItem {\n                viewModel.updateScene(QMUIPhotoPickerGridScene)\n            })\n        }\n\n        val topBarRightItems = remember(config) {\n            arrayListOf(config.topBarSendFactory(true, viewModel.pickLimitCount, viewModel.pickedCountFlow) {\n                val pickedList = viewModel.getPickedResultList()\n                if(pickedList.isEmpty()){\n                    onHandleSend(listOf(data[pagerState.currentPage].let {\n                        QMUIPhotoPickItemInfo(\n                            it.model.id,\n                            it.model.name,\n                            it.model.width,\n                            it.model.height,\n                            it.model.uri,\n                            it.model.rotation\n                        )\n                    }))\n                }else{\n                    onHandleSend(pickedList)\n                }\n\n            })\n        }\n\n        val scope = rememberCoroutineScope()\n\n        Box(modifier = Modifier.fillMaxSize()) {\n            QMUIPhotoPickerPreview(\n                pagerState,\n                data,\n                loading = { Loading() },\n                loadingFailed = {},\n            ) {\n                isFullPageState = !isFullPageState\n            }\n\n            AnimatedVisibility(\n                visible = !isFullPageState,\n                enter = slideInVertically(initialOffsetY = { -it }),\n                exit = slideOutVertically(targetOffsetY = { -it })\n            ) {\n                QMUITopBar(\n                    title = \"${pagerState.currentPage + 1}/${data.size}\",\n                    separatorHeight = 0.dp,\n                    paddingEnd = 16.dp,\n                    backgroundColor = QMUILocalPickerConfig.current.topBarBgColor,\n                    leftItems = topBarLeftItems,\n                    rightItems = topBarRightItems\n                )\n            }\n\n            AnimatedVisibility(\n                visible = !isFullPageState,\n                modifier = Modifier.align(Alignment.BottomCenter),\n                enter = slideInVertically(initialOffsetY = { it }),\n                exit = slideOutVertically(targetOffsetY = { it })\n            ) {\n                Column(modifier = Modifier.fillMaxWidth()) {\n                    QMUIPhotoPickerPreviewPickedItems(data, pickedItems, data[pagerState.currentPage].model.id) {\n                        scope.launch {\n                            pagerState.scrollToPage(data.indexOf(it))\n                        }\n                    }\n\n                    val isCurrentPicked = remember(data, pickedItems, pagerState.currentPage) {\n                        pickedItems.indexOf(data[pagerState.currentPage].model.id) >= 0\n                    }\n\n                    QMUIPhotoPickerPreviewToolBar(\n                        modifier = Modifier.fillMaxWidth(),\n                        current = data[pagerState.currentPage],\n                        isCurrentPicked = isCurrentPicked,\n                        enableOrigin = viewModel.enableOrigin,\n                        isOriginOpenFlow = viewModel.isOriginOpenFlow,\n                        onToggleOrigin = {\n                            viewModel.toggleOrigin(it)\n                        },\n                        onEdit = {\n                            viewModel.updateScene(QMUIPhotoPickerEditScene(data[pagerState.currentPage]))\n                        },\n                        onToggleSelect = {\n                            viewModel.togglePick(data[pagerState.currentPage])\n                        }\n                    )\n\n                }\n            }\n        }\n    }\n\n\n    @Composable\n    protected open fun BoxScope.PhotoPickerEditScene(\n        viewModel: QMUIPhotoPickerViewModel,\n        scene: QMUIPhotoPickerEditScene\n    ) {\n        LaunchedEffect(\"\") {\n            WindowCompat.getInsetsController(window, window.decorView)?.hide(WindowInsetsCompat.Type.statusBars())\n        }\n        QMUIPhotoPickerEdit(onBackPressedDispatcher, scene.current) {\n            viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene)\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.Loading() {\n        Box(modifier = Modifier.align(Alignment.Center)) {\n            QMUIPhotoLoading(lineColor = QMUILocalPickerConfig.current.loadingColor)\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.PermissionDenied() {\n        CommonTip(text = \"选择图片需要存储权限\\n请先前往设置打开存储权限\")\n    }\n\n    @Composable\n    protected open fun BoxScope.PageError(throwable: Throwable) {\n        val text = if (QMUIGlobal.debug) {\n            \"读取数据发生错误, ${throwable.message}\"\n        } else {\n            \"读取数据发生错误\"\n        }\n        CommonTip(text = text)\n    }\n\n    @Composable\n    protected open fun BoxScope.PageEmpty() {\n        CommonTip(text = \"你的相册空空如也~\")\n    }\n\n    @Composable\n    protected open fun BoxScope.CommonTip(text: String) {\n        Box(\n            modifier = Modifier\n                .align(Alignment.Center)\n                .padding(20.dp)\n        ) {\n            Text(\n                text,\n                fontSize = 16.sp,\n                color = QMUILocalPickerConfig.current.tipTextColor,\n                textAlign = TextAlign.Center,\n                lineHeight = 20.sp\n            )\n        }\n    }\n\n    protected open fun onHandleSend(pickedList: List<QMUIPhotoPickItemInfo>) {\n        setResult(RESULT_OK, Intent().apply {\n            putParcelableArrayListExtra(QMUI_PHOTO_RESULT_URI_LIST, arrayListOf<QMUIPhotoPickItemInfo>().apply {\n                addAll(pickedList)\n            })\n            putExtra(QMUI_PHOTO_RESULT_ORIGIN_OPEN, viewModel.isOriginOpenFlow.value)\n        })\n        finish()\n    }\n\n    protected open fun onStartCheckPermission() {\n        permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)\n    }\n\n    protected open fun onHandlePermissionResult(granted: Boolean) {\n        if (granted) {\n            viewModel.permissionGranted()\n        } else {\n            viewModel.permissionDenied()\n        }\n    }\n\n    protected open fun dataProvider(): QMUIMediaDataProvider {\n        return QMUIMediaImagesProvider()\n    }\n\n    protected open fun supportedMimeTypes(): Array<String> {\n        return QMUIMediaImagesProvider.DEFAULT_SUPPORT_MIMETYPES\n    }\n\n    private class SceneHolder<T:QMUIPhotoPickerScene>(var scene: T? = null)\n}\n\n\n"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/activity/QMUIPhotoViewerActivity.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.photo.activity\n\nimport android.content.Intent\nimport android.graphics.drawable.Drawable\nimport android.os.Build\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.WindowManager\nimport androidx.activity.ComponentActivity\nimport androidx.activity.OnBackPressedCallback\nimport androidx.activity.compose.setContent\nimport androidx.activity.viewModels\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.compose.animation.core.Transition\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.graphics.painter.BitmapPainter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.core.graphics.drawable.toBitmap\nimport androidx.core.view.WindowCompat\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport com.google.accompanist.pager.ExperimentalPagerApi\nimport com.google.accompanist.pager.HorizontalPager\nimport com.google.accompanist.pager.PagerScope\nimport com.google.accompanist.pager.rememberPagerState\nimport com.qmuiteam.compose.core.helper.QMUILog\nimport com.qmuiteam.photo.R\nimport com.qmuiteam.photo.compose.QMUIDefaultPhotoConfigProvider\nimport com.qmuiteam.photo.compose.QMUIGesturePhoto\nimport com.qmuiteam.photo.compose.QMUIPhotoLoading\nimport com.qmuiteam.photo.data.*\nimport com.qmuiteam.photo.util.asBitmap\nimport kotlinx.coroutines.flow.MutableStateFlow\n\nprivate const val PHOTO_CURRENT_INDEX = \"qmui_photo_current_index\"\nprivate const val PHOTO_TRANSITION_DELIVERY_KEY = \"qmui_photo_transition_delivery\"\nprivate const val PHOTO_COUNT = \"qmui_photo_count\"\nprivate const val PHOTO_META_KEY_PREFIX = \"qmui_photo_meta_\"\nprivate const val PHOTO_PROVIDER_RECOVER_CLASS_KEY_PREFIX = \"qmui_photo_provider_recover_cls_\"\n\nopen class QMUIPhotoViewerActivity : AppCompatActivity() {\n\n    companion object {\n\n        fun intentOf(\n            activity: ComponentActivity,\n            cls: Class<out QMUIPhotoViewerActivity>,\n            list: List<QMUIPhotoTransitionInfo>,\n            index: Int\n        ): Intent {\n            val data = PhotoViewerData(list, index, activity.window.decorView.asBitmap())\n            val intent = Intent(activity, cls)\n            intent.putExtra(PHOTO_TRANSITION_DELIVERY_KEY, QMUIPhotoTransitionDelivery.put(data))\n            intent.putExtra(PHOTO_CURRENT_INDEX, index)\n            intent.putExtra(PHOTO_COUNT, list.size)\n            if(list.size < 250){\n                list.forEachIndexed { i, transition ->\n                    val meta = transition.photoProvider.meta()\n                    val recoverCls = transition.photoProvider.recoverCls()\n                    if (meta != null && recoverCls != null) {\n                        intent.putExtra(\"${PHOTO_META_KEY_PREFIX}${i}\", meta)\n                        intent.putExtra(\n                            \"${PHOTO_PROVIDER_RECOVER_CLASS_KEY_PREFIX}${i}\",\n                            recoverCls.name\n                        )\n                    }\n                }\n            } else {\n                QMUILog.w(\"QMUIPhotoViewerActivity\", \"once delivered too many photos, so only use memory data for delivery, there may be some recover issue.\")\n            }\n\n            return intent\n        }\n    }\n\n    private val viewModel by viewModels<QMUIPhotoViewerViewModel>()\n    private val transitionTargetFlow = MutableStateFlow(true)\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n        WindowCompat.getInsetsController(window, window.decorView)?.let {\n            it.hide(WindowInsetsCompat.Type.statusBars())\n            it.isAppearanceLightNavigationBars = false\n        }\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {\n            window.navigationBarColor = android.graphics.Color.TRANSPARENT\n            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {\n                window.navigationBarDividerColor = android.graphics.Color.TRANSPARENT\n                window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES\n            }\n        }\n\n        onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {\n            override fun handleOnBackPressed() {\n                transitionTargetFlow.value = false\n            }\n        })\n\n        setContent {\n            PageContent()\n        }\n    }\n\n    @Composable\n    protected open fun PageContent() {\n        Box(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            val data = viewModel.data\n            if (data == null || data.list.isEmpty()) {\n                Text(text = \"没有图片数据\")\n            } else {\n                viewModel.data?.background?.let {\n                    Image(\n                        painter = BitmapPainter(it.asImageBitmap()),\n                        contentDescription = \"\",\n                        contentScale = ContentScale.FillWidth,\n                        alignment = Alignment.TopCenter,\n                        modifier = Modifier.fillMaxSize()\n                    )\n                }\n                PhotoViewerProviderWrapper(list = data.list, index = data.index)\n            }\n        }\n    }\n\n    @Composable\n    protected open fun PhotoViewerProviderWrapper(list: List<QMUIPhotoTransitionInfo>, index: Int) {\n        QMUIDefaultPhotoConfigProvider {\n            PhotoViewer(list, index)\n        }\n    }\n\n    @OptIn(ExperimentalPagerApi::class)\n    @Composable\n    protected open fun PhotoViewer(list: List<QMUIPhotoTransitionInfo>, index: Int) {\n        val pagerState = rememberPagerState(index)\n        HorizontalPager(\n            count = list.size,\n            state = pagerState\n        ) { page ->\n            PhotoPage(page, list[page], page == index)\n        }\n    }\n\n    protected open fun pullExitMiniTranslateY(): Dp = 72.dp\n\n    @OptIn(ExperimentalPagerApi::class)\n    @Composable\n    protected open fun PagerScope.PhotoPage(page: Int, item: QMUIPhotoTransitionInfo, shouldTransitionEnter: Boolean) {\n        val initRect = item.photoRect()\n        val transitionTarget = if (currentPage == page) {\n            transitionTargetFlow.collectAsState().value\n        } else true\n        val drawableCache = remember {\n            MutableDrawableCache()\n        }\n        BoxWithConstraints(modifier = Modifier.fillMaxSize()) {\n            PhotoGestureWrapper(item) { photoLoadCallback ->\n                QMUIGesturePhoto(\n                    containerWidth = maxWidth,\n                    containerHeight = maxHeight,\n                    imageRatio = item.ratio(),\n                    isLongImage = item.photoProvider.isLongImage(),\n                    initRect = initRect,\n                    shouldTransitionEnter = shouldTransitionEnter && shouldTransitionPhoto(),\n                    shouldTransitionExit = shouldTransitionPhoto(),\n                    transitionTarget = transitionTarget,\n                    pullExitMiniTranslateY = pullExitMiniTranslateY(),\n                    onBeginPullExit = {\n                        allowPullExit()\n                    },\n                    onLongPress = {\n                        drawableCache.drawable?.let {\n                            onLongClick(page, it)\n                        }\n                    },\n                    onTapExit = {\n                        onTapExit(page, it)\n                    }\n                ) { transition, _, _, onImageRatioEnsured ->\n\n                    val onPhotoLoad: (PhotoResult) -> Unit = remember(drawableCache, onImageRatioEnsured) {\n                        {\n                            drawableCache.drawable = it.drawable\n                            if (it.drawable.intrinsicWidth > 0 && it.drawable.intrinsicHeight > 0) {\n                                onImageRatioEnsured(it.drawable.intrinsicWidth.toFloat() / it.drawable.intrinsicHeight)\n                            }\n                            photoLoadCallback?.invoke(it)\n                        }\n                    }\n\n                    PhotoContent(\n                        transition = transition,\n                        photoTransitionInfo = item,\n                        onPhotoLoaded = onPhotoLoad\n                    )\n                }\n            }\n        }\n    }\n\n    @Composable\n    protected open fun BoxWithConstraintsScope.PhotoGestureWrapper(\n        item: QMUIPhotoTransitionInfo,\n        content: @Composable BoxWithConstraintsScope.(onPhotoLoaded: ((PhotoResult) -> Unit)?)->Unit\n    ){\n        content(null)\n    }\n\n    @Composable\n    protected open fun PhotoContent(\n        transition: Transition<Boolean>,\n        photoTransitionInfo: QMUIPhotoTransitionInfo,\n        onPhotoLoaded: (PhotoResult) -> Unit\n    ) {\n        DefaultPhotoContent(transition, photoTransitionInfo, onPhotoLoaded)\n    }\n\n    @Composable\n    protected fun DefaultPhotoContent(\n        transition: Transition<Boolean>,\n        photoTransitionInfo: QMUIPhotoTransitionInfo,\n        onPhotoLoaded: (PhotoResult) -> Unit\n    ){\n        val thumb = remember(photoTransitionInfo) { photoTransitionInfo.photoProvider.thumbnail(false) }\n\n        var loadStatus by remember {\n            mutableStateOf(PhotoLoadStatus.loading)\n        }\n\n        val onSuccess: (PhotoResult) -> Unit = remember(onPhotoLoaded) {\n            {\n                onPhotoLoaded(it)\n                loadStatus = PhotoLoadStatus.success\n            }\n        }\n\n        Box(modifier = Modifier.fillMaxSize()) {\n            PhotoItem(photoTransitionInfo,\n                onSuccess = onSuccess,\n                onError = {\n                    loadStatus = PhotoLoadStatus.failed\n                }\n            )\n\n            if (loadStatus != PhotoLoadStatus.success || !transition.currentState || !transition.targetState) {\n                val transitionPhoto = photoTransitionInfo.photo\n                val contentScale = when {\n                    photoTransitionInfo.photoProvider.isLongImage() -> {\n                        ContentScale.FillWidth\n                    }\n                    photoTransitionInfo.ratio() > 0f &&\n                            photoTransitionInfo.offsetInWindow != null &&\n                            photoTransitionInfo.size != null -> {\n                        ContentScale.Crop\n                    }\n                    else -> ContentScale.Fit\n                }\n                if (transitionPhoto != null) {\n                    Image(\n                        painter = BitmapPainter(transitionPhoto.toBitmap().asImageBitmap()),\n                        contentDescription = \"\",\n                        alignment = if (photoTransitionInfo.photoProvider.isLongImage()) Alignment.TopCenter else Alignment.Center,\n                        contentScale = contentScale,\n                        modifier = Modifier.fillMaxSize()\n                    )\n                } else {\n                    thumb?.Compose(\n                        contentScale = contentScale,\n                        isContainerDimenExactly = true,\n                        onSuccess = null,\n                        onError = null\n                    )\n                }\n            }\n\n            if (loadStatus == PhotoLoadStatus.loading) {\n                Loading()\n            } else if (loadStatus == PhotoLoadStatus.failed) {\n                LoadingFailed()\n            }\n        }\n    }\n\n    @Composable\n    private fun PhotoItem(\n        photoTransitionInfo: QMUIPhotoTransitionInfo,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)? = null\n    ) {\n        val photo = remember(photoTransitionInfo) {\n            photoTransitionInfo.photoProvider.photo()\n        }\n        photo?.Compose(\n            contentScale = ContentScale.Fit,\n            isContainerDimenExactly = true,\n            onSuccess = onSuccess,\n            onError = onError\n        )\n    }\n\n    @Composable\n    protected open fun BoxScope.Loading() {\n        Box(modifier = Modifier.align(Alignment.Center)) {\n            QMUIPhotoLoading(size = 48.dp)\n        }\n    }\n\n    @Composable\n    protected open fun BoxScope.LoadingFailed() {\n        // do nothing default, users should handle load fail / reload in Photo\n    }\n\n    protected open fun shouldTransitionPhoto(): Boolean {\n        return true\n    }\n\n    protected open fun allowPullExit(): Boolean {\n        return true\n    }\n\n    protected open fun onLongClick(page: Int, drawable: Drawable) {\n\n    }\n\n    protected open fun onTapExit(page: Int, afterTransition: Boolean) {\n        if (afterTransition) {\n            finish()\n            overridePendingTransition(0, 0)\n        } else {\n            finish()\n            overridePendingTransition(0, R.anim.scale_exit)\n        }\n    }\n}\n\n\nclass QMUIPhotoViewerViewModel(val state: SavedStateHandle) : ViewModel() {\n\n    val enterIndex = state.get<Int>(PHOTO_CURRENT_INDEX) ?: 0\n    val data: PhotoViewerData?\n\n    private val transitionDeliverKey = state.get<Long>(PHOTO_TRANSITION_DELIVERY_KEY) ?: -1\n\n    init {\n        val transitionDeliverData = QMUIPhotoTransitionDelivery.getAndRemove(transitionDeliverKey)\n        data = if (transitionDeliverData != null) {\n            transitionDeliverData\n        } else {\n            val count = state.get<Int>(PHOTO_COUNT) ?: 0\n            if (count > 0) {\n                val list = arrayListOf<QMUIPhotoTransitionInfo>()\n                for (i in 0 until count) {\n                    try {\n                        val meta = state.get<Bundle>(\"${PHOTO_META_KEY_PREFIX}${i}\")\n                        val clsName =\n                            state.get<String>(\"${PHOTO_PROVIDER_RECOVER_CLASS_KEY_PREFIX}${i}\")\n                        if (meta == null || clsName.isNullOrBlank()) {\n                            list.add(lossPhotoTransitionInfo)\n                        } else {\n                            val cls = Class.forName(clsName)\n                            val recover = cls.newInstance() as PhotoTransitionProviderRecover\n                            list.add(recover.recover(meta) ?: lossPhotoTransitionInfo)\n                        }\n\n                    } catch (e: Throwable) {\n                        list.add(lossPhotoTransitionInfo)\n                    }\n                }\n                PhotoViewerData(list, enterIndex, null)\n            } else {\n                null\n            }\n        }\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        QMUIPhotoTransitionDelivery.remove(transitionDeliverKey)\n    }\n}\n\nclass MutableDrawableCache(var drawable: Drawable? = null)\n"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/BitmapRegion.kt",
    "content": "package com.qmuiteam.photo.compose\n\nimport android.graphics.Bitmap\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.graphics.painter.BitmapPainter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.Dp\nimport com.qmuiteam.photo.data.QMUIBitmapRegionProvider\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\n@Composable\nfun QMUIBitmapRegionItem(bmRegion: QMUIBitmapRegionProvider, w: Dp, h: Dp) {\n    var bitmap by remember {\n        mutableStateOf<Bitmap?>(null)\n    }\n    LaunchedEffect(key1 = bmRegion) {\n        withContext(Dispatchers.IO) {\n            bitmap = bmRegion.loader.load()\n        }\n    }\n    Box(modifier = Modifier.size(w, h)) {\n        val bm = bitmap\n        if (bm != null) {\n            Image(\n                painter = BitmapPainter(bm.asImageBitmap()),\n                contentDescription = \"\",\n                contentScale = ContentScale.FillWidth,\n                modifier = Modifier.fillMaxSize()\n            )\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/GesturePhoto.kt",
    "content": "package com.qmuiteam.photo.compose\n\nimport android.util.Log\nimport androidx.compose.animation.core.*\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.gestures.*\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.geometry.toRect\nimport androidx.compose.ui.graphics.*\nimport androidx.compose.ui.input.nestedscroll.NestedScrollConnection\nimport androidx.compose.ui.input.nestedscroll.NestedScrollSource\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.input.pointer.consumeAllChanges\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.input.pointer.positionChangeConsumed\nimport androidx.compose.ui.input.pointer.positionChanged\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.LayoutDirection\nimport androidx.compose.ui.unit.dp\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.launch\nimport kotlin.math.abs\nimport kotlin.math.absoluteValue\n\n@Composable\nfun QMUIGesturePhoto(\n    containerWidth: Dp,\n    containerHeight: Dp,\n    imageRatio: Float,\n    isLongImage: Boolean,\n    initRect: Rect? = null,\n    shouldTransitionEnter: Boolean = false,\n    shouldTransitionExit: Boolean = true,\n    transitionTarget: Boolean = true,\n    transitionDurationMs: Int = 360,\n    pullExitMiniTranslateY: Dp = 72.dp,\n    panEdgeProtection: Rect = Rect(\n        0f,\n        0f,\n        with(LocalDensity.current) { containerWidth.toPx() },\n        with(LocalDensity.current) { containerHeight.toPx() }),\n    maxScale: Float = 4f,\n    onPress: suspend PressGestureScope.(Offset) -> Unit = { },\n    onBeginPullExit: () -> Boolean,\n    onLongPress: (() -> Unit)? = null,\n    onTapExit: (afterTransition: Boolean) -> Unit,\n    content: @Composable (transition: Transition<Boolean>, scale: Float, rect: Rect, onImageRatioEnsured: (Float) -> Unit) -> Unit\n) {\n\n    val (imageWidth, imageHeight) = calculateImageSize(containerWidth, containerHeight, imageRatio, isLongImage)\n\n    var calculatedImageRatio by remember {\n        mutableStateOf(imageRatio)\n    }\n\n    val density = LocalDensity.current\n    val imagePaddingFix by remember(density, panEdgeProtection, isLongImage, containerWidth, containerHeight, calculatedImageRatio, imageRatio) {\n        val (expectWidth, expectHeight) = calculateImageSize(containerWidth, containerHeight, calculatedImageRatio, isLongImage)\n        val widthPadding = with(density) {\n            (imageWidth - expectWidth).toPx() / 2\n        }\n        val heightPadding = with(density) {\n            (imageHeight - expectHeight).toPx() / 2\n        }\n\n        mutableStateOf(widthPadding to heightPadding)\n    }\n\n    val usedImageRatioUpdater = remember {\n        val func: (Float) -> Unit = { value ->\n            if (value > 0) {\n                calculatedImageRatio = value\n            }\n        }\n        func\n    }\n\n\n    var backgroundTargetAlpha by remember {\n        mutableStateOf(1f)\n    }\n\n    val photoTargetNormalTranslateX = with(LocalDensity.current) {\n        ((containerWidth - imageWidth) / 2f).toPx()\n    }\n\n    val photoTargetNormalTranslateY = with(LocalDensity.current) {\n        ((containerHeight - imageHeight) / 2f).toPx()\n    }\n\n    var photoTargetScale by remember(containerWidth, containerHeight) { mutableStateOf(1f) }\n    var photoTargetTranslateX by remember(containerWidth, containerHeight) { mutableStateOf(photoTargetNormalTranslateX) }\n    var photoTargetTranslateY by remember(containerWidth, containerHeight) { mutableStateOf(photoTargetNormalTranslateY) }\n\n    val containerWidthPx = with(LocalDensity.current) { containerWidth.toPx() }\n    val containerHeightPx = with(LocalDensity.current) { containerHeight.toPx() }\n    val imageWidthPx = with(LocalDensity.current) { imageWidth.toPx() }\n    val imageHeightPx = with(LocalDensity.current) { imageHeight.toPx() }\n    var isGestureHandling by remember(containerWidth, containerHeight) {\n        mutableStateOf(false)\n    }\n\n    var transitionTargetState by remember(containerWidth, containerHeight, transitionTarget) { mutableStateOf(transitionTarget) }\n    val transitionState = remember(containerWidth, containerHeight) {\n        MutableTransitionState(!shouldTransitionEnter)\n    }\n\n    val scaleHandler: (Offset, Float, Boolean) -> Unit = remember(containerWidth, containerHeight, maxScale, imageRatio) {\n        lambda@{ center, scaleParam, edgeProtection ->\n            var scale = scaleParam\n            if (photoTargetScale * scaleParam > maxScale) {\n                scale = maxScale / photoTargetScale\n            }\n            if (scale == 1f) {\n                return@lambda\n            }\n            var targetLeft = center.x + ((photoTargetTranslateX - center.x) * scale)\n            var targetTop = center.y + ((photoTargetTranslateY - center.y) * scale)\n            val targetWidth = imageWidthPx * photoTargetScale * scale\n            val targetHeight = imageHeightPx * photoTargetScale * scale\n\n            if (edgeProtection) {\n                when {\n                    containerWidthPx > targetWidth -> {\n                        targetLeft = (containerWidthPx - targetWidth) / 2\n                    }\n                    targetLeft > 0 -> {\n                        targetLeft = 0f\n                    }\n                    targetLeft + targetWidth < containerWidthPx -> {\n                        targetLeft = containerWidthPx - targetWidth\n                    }\n                }\n\n                when {\n                    containerHeightPx > targetHeight -> {\n                        targetTop = (containerHeightPx - targetHeight) / 2\n                    }\n                    targetTop > 0 -> {\n                        targetTop = 0f\n                    }\n                    targetTop + targetHeight < containerHeightPx -> {\n                        targetTop = containerHeightPx - targetHeight\n                    }\n                }\n            }\n            photoTargetTranslateX = targetLeft\n            photoTargetTranslateY = targetTop\n            photoTargetScale *= scale\n        }\n    }\n\n    val reset: () -> Unit = remember(containerWidth, containerHeight, imageRatio) {\n        {\n            backgroundTargetAlpha = 1f\n            photoTargetScale = 1f\n            photoTargetTranslateX = photoTargetNormalTranslateX\n            photoTargetTranslateY = photoTargetNormalTranslateY\n        }\n    }\n\n    transitionState.targetState = transitionTargetState\n    val transition = updateTransition(transitionState = transitionState, label = \"PhotoPager\")\n\n    val nestedScrollConnection = remember {\n        GestureNestScrollConnection()\n    }\n\n    Box(\n        modifier = Modifier\n            .width(containerWidth)\n            .height(containerHeight)\n    ) {\n        PhotoBackgroundWithTransition(backgroundTargetAlpha, transition, transitionDurationMs) {\n            PhotoBackground(alpha = it)\n        }\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .nestedScroll(nestedScrollConnection)\n                .pointerInput(containerWidth, containerHeight, maxScale, shouldTransitionExit, onTapExit, onBeginPullExit, imagePaddingFix) {\n                    coroutineScope {\n                        launch {\n                            detectTapGestures(\n                                onTap = {\n                                    if (shouldTransitionExit) {\n                                        transitionTargetState = false\n                                    } else {\n                                        onTapExit(false)\n                                    }\n                                },\n                                onLongPress = {\n                                    onLongPress?.invoke()\n                                },\n                                onDoubleTap = {\n                                    if (photoTargetScale == 1f) {\n                                        var scale = 2f\n                                        val alignScale = (containerWidth / imageWidth).coerceAtLeast((containerHeight / imageHeight))\n                                        if (alignScale > 1.25 && alignScale < scale) {\n                                            scale = alignScale\n                                        }\n                                        scaleHandler.invoke(it, scale, true)\n                                    } else {\n                                        reset()\n                                    }\n                                },\n                                onPress = onPress\n                            )\n                        }\n\n                        launch {\n                            forEachGesture {\n                                awaitPointerEventScope {\n                                    var zoom = 1f\n                                    var pan = Offset.Zero\n                                    val touchSlop = viewConfiguration.touchSlop\n                                    var isZooming = false\n                                    var isPanning = false\n                                    var isExitPanning = false\n                                    isGestureHandling = false\n                                    awaitFirstDown(requireUnconsumed = false)\n                                    nestedScrollConnection.canConsumeEvent = false\n                                    nestedScrollConnection.isIntercepted = false\n                                    do {\n                                        val event = awaitPointerEvent()\n                                        if (isZooming || isExitPanning) {\n                                            nestedScrollConnection.isIntercepted = true\n                                        }\n                                        val needHandle = nestedScrollConnection.canConsumeEvent || event.changes.none { it.positionChangeConsumed() }\n                                        if (needHandle) {\n                                            val zoomChange = event.calculateZoom()\n                                            val panChange = event.calculatePan()\n\n                                            if (!isZooming && !isPanning) {\n                                                zoom *= zoomChange\n                                                pan += panChange\n\n                                                val centroidSize = event.calculateCentroidSize(useCurrent = false)\n                                                val zoomMotion = abs(1 - zoom) * centroidSize\n                                                val panMotion = pan.getDistance()\n\n                                                if (zoomMotion > touchSlop) {\n                                                    isGestureHandling = true\n                                                    isZooming = true\n                                                } else if (panMotion > touchSlop) {\n                                                    isPanning = true\n                                                    isGestureHandling = true\n                                                }\n                                            }\n\n                                            if (isZooming) {\n                                                val centroid = event.calculateCentroid(useCurrent = false)\n                                                if (zoomChange != 1f) {\n                                                    scaleHandler(centroid, zoomChange, true)\n                                                }\n                                                event.changes.forEach {\n                                                    if (it.positionChanged()) {\n                                                        it.consumeAllChanges()\n                                                    }\n                                                }\n                                            } else if (isPanning) {\n                                                if (!isExitPanning) {\n                                                    var xConsumed = false\n                                                    var yConsumed = false\n                                                    if (panChange != Offset.Zero) {\n                                                        if (panChange.x > 0) {\n                                                            val fixEdgeLeft = panEdgeProtection.left - imagePaddingFix.first * photoTargetScale\n                                                            if (photoTargetTranslateX < fixEdgeLeft) {\n                                                                photoTargetTranslateX =\n                                                                    (photoTargetTranslateX + panChange.x).coerceAtMost(fixEdgeLeft)\n                                                                xConsumed = true\n                                                            }\n                                                        }\n                                                        if (panChange.x < 0) {\n                                                            val w = imageWidthPx * photoTargetScale\n                                                            val fixEdgeRight = panEdgeProtection.right + imagePaddingFix.first * photoTargetScale\n                                                            if (photoTargetTranslateX + w > fixEdgeRight) {\n                                                                photoTargetTranslateX =\n                                                                    (photoTargetTranslateX + panChange.x).coerceAtLeast(fixEdgeRight - w)\n                                                                xConsumed = true\n                                                            }\n                                                        }\n\n                                                        if (panChange.y > 0) {\n                                                            val fixEdgeTop = panEdgeProtection.top - imagePaddingFix.second * photoTargetScale\n                                                            if (photoTargetTranslateY < fixEdgeTop) {\n                                                                photoTargetTranslateY = (photoTargetTranslateY + panChange.y).coerceAtMost(fixEdgeTop)\n                                                                yConsumed = true\n                                                            } else if (!xConsumed && panChange.y > panChange.x.absoluteValue) {\n                                                                isExitPanning = photoTargetScale == 1f && onBeginPullExit()\n                                                            }\n                                                        }\n\n                                                        if (panChange.y < 0) {\n                                                            val h = imageHeightPx * photoTargetScale\n                                                            val fixEgeBottom = panEdgeProtection.bottom + imagePaddingFix.second * photoTargetScale\n                                                            if (photoTargetTranslateY + h > fixEgeBottom) {\n                                                                photoTargetTranslateY =\n                                                                    (photoTargetTranslateY + panChange.y).coerceAtLeast(fixEgeBottom - h)\n                                                                yConsumed = true\n                                                            }\n                                                        }\n                                                    }\n\n                                                    if (xConsumed || yConsumed) {\n                                                        event.changes.forEach {\n                                                            if (it.positionChanged()) {\n                                                                it.consumeAllChanges()\n                                                            }\n                                                        }\n                                                    }\n                                                }\n\n                                                if (isExitPanning) {\n                                                    val center = event.calculateCentroid(useCurrent = true)\n                                                    val scaleChange = 1 - panChange.y / containerHeightPx / 2\n                                                    val finalScale = (photoTargetScale * scaleChange)\n                                                        .coerceAtLeast(0.5f)\n                                                        .coerceAtMost(1f)\n                                                    backgroundTargetAlpha = finalScale\n                                                    photoTargetTranslateX += panChange.x\n                                                    photoTargetTranslateY += panChange.y\n                                                    scaleHandler(center, finalScale / photoTargetScale, false)\n                                                    event.changes.forEach {\n                                                        if (it.positionChanged()) {\n                                                            it.consumeAllChanges()\n                                                        }\n                                                    }\n                                                }\n                                            }\n                                        }\n                                    } while (event.changes.any { it.pressed })\n\n                                    isGestureHandling = false\n                                    if (isZooming) {\n                                        if (photoTargetScale < 1f) {\n                                            reset()\n                                        }\n                                    }\n\n                                    if (isExitPanning) {\n                                        if (photoTargetTranslateY - photoTargetNormalTranslateY < pullExitMiniTranslateY.toPx()) {\n                                            reset()\n                                        } else {\n                                            transitionTargetState = false\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n        ) {\n\n            if (initRect == null || initRect == Rect.Zero || imageRatio <= 0f) {\n                PhotoContentWithAlphaTransition(\n                    transition = transition,\n                    transitionDurationMs = transitionDurationMs,\n                    isGestureHandling = isGestureHandling,\n                    scale = photoTargetScale,\n                    translateX = photoTargetTranslateX,\n                    translateY = photoTargetTranslateY\n                ) { alpha, scale, translateX, translateY ->\n                    PhotoTransformContent(\n                        alpha,\n                        imageWidthPx,\n                        imageHeightPx,\n                        scale,\n                        scale,\n                        translateX,\n                        translateY\n                    ) {\n                        val imageLeft = translateX + imagePaddingFix.first * it\n                        val imageTop = translateY + imagePaddingFix.second * it\n                        content(\n                            transition,\n                            it,\n                            Rect(imageLeft, imageTop, imageLeft + imageWidthPx * it, imageTop + imageHeightPx * it),\n                            usedImageRatioUpdater\n                        )\n                    }\n                }\n            } else {\n                PhotoContentWithRectTransition(\n                    imageWidth = imageWidthPx,\n                    imageHeight = imageHeightPx,\n                    initRect = initRect,\n                    scale = photoTargetScale,\n                    translateX = photoTargetTranslateX,\n                    translateY = photoTargetTranslateY,\n                    transition = transition,\n                    transitionDurationMs = transitionDurationMs\n                ) { scaleX, scaleY, translateX, translateY ->\n                    PhotoTransformContent(1f, imageWidthPx, imageHeightPx, scaleX, scaleY, translateX, translateY) {\n                        val imageLeft = translateX + imagePaddingFix.first * it\n                        val imageTop = translateY + imagePaddingFix.second * it\n                        content(\n                            transition,\n                            it,\n                            Rect(imageLeft, imageTop, imageLeft + imageWidthPx * it, imageTop + imageHeightPx * it),\n                            usedImageRatioUpdater\n                        )\n                    }\n                }\n            }\n        }\n    }\n\n\n    if (!transitionState.currentState && !transitionState.targetState) {\n        onTapExit(true)\n    }\n}\n\n@Composable\nfun PhotoBackgroundWithTransition(\n    backgroundTargetAlpha: Float,\n    transition: Transition<Boolean>,\n    transitionDurationMs: Int,\n    content: @Composable (alpha: Float) -> Unit\n) {\n    val alpha = transition.animateFloat(\n        transitionSpec = { tween(durationMillis = transitionDurationMs) },\n        label = \"PhotoBackgroundWithTransition\"\n    ) {\n        if (it) backgroundTargetAlpha else 0f\n    }\n    content(alpha.value)\n}\n\n@Composable\nfun PhotoContentWithAlphaTransition(\n    transition: Transition<Boolean>,\n    transitionDurationMs: Int,\n    isGestureHandling: Boolean,\n    scale: Float,\n    translateX: Float,\n    translateY: Float,\n    content: @Composable (alpha: Float, scale: Float, translateX: Float, translateY: Float) -> Unit\n) {\n    val alphaState = transition.animateFloat(\n        transitionSpec = { tween(durationMillis = transitionDurationMs) },\n        label = \"PhotoContentWithAlphaTransition\"\n    ) {\n        if (it) 1f else 0f\n    }\n    val duration = if (isGestureHandling) 0 else transitionDurationMs\n    val scaleState = animateFloatAsState(\n        targetValue = scale,\n        animationSpec = tween(durationMillis = duration)\n    )\n    val translateXState = animateFloatAsState(\n        targetValue = translateX,\n        animationSpec = tween(durationMillis = duration)\n    )\n    val translateYState = animateFloatAsState(\n        targetValue = translateY,\n        animationSpec = tween(durationMillis = duration)\n    )\n    content(alphaState.value, scaleState.value, translateXState.value, translateYState.value)\n}\n\n@Composable\nfun PhotoContentWithRectTransition(\n    imageWidth: Float,\n    imageHeight: Float,\n    initRect: Rect,\n    scale: Float,\n    translateX: Float,\n    translateY: Float,\n    transition: Transition<Boolean>,\n    transitionDurationMs: Int,\n    content: @Composable (scaleX: Float, scaleY: Float, translateX: Float, translateY: Float) -> Unit\n) {\n    val rect = transition.animateRect(\n        transitionSpec = { tween(durationMillis = transitionDurationMs) },\n        label = \"PhotoContentWithRectTransition\"\n    ) {\n        if (it) Rect(translateX, translateY, translateX + imageWidth * scale, translateY + imageHeight * scale) else initRect\n    }\n    content(\n        (rect.value.width / imageWidth).coerceAtLeast(0f),\n        (rect.value.height / imageHeight).coerceAtLeast(0f),\n        rect.value.left,\n        rect.value.top\n    )\n\n}\n\n@Composable\nfun PhotoBackground(\n    alpha: Float\n) {\n    Box(\n        modifier = Modifier\n            .fillMaxSize()\n            .alpha(alpha)\n            .background(Color.Black)\n    )\n}\n\n@Composable\nfun PhotoTransformContent(\n    alpha: Float,\n    width: Float,\n    height: Float,\n    scaleX: Float,\n    scaleY: Float,\n    translateX: Float,\n    translateY: Float,\n    content: @Composable (scale: Float) -> Unit\n) {\n    val widthDp = with(LocalDensity.current) { width.toDp() }\n    val heightDp = with(LocalDensity.current) { height.toDp() }\n    val scale = scaleX.coerceAtLeast(scaleY)\n    val clipSize = remember(scaleX, scaleY, width, height) {\n        if(scale == 0f){\n            Size(0f, 0f)\n        }else{\n            val expectedW = width * scaleX / scale\n            val expectedH = height * scaleY / scale\n            val clipW = (width - expectedW) / 2\n            val clipH = (height - expectedH) / 2\n            Size(clipW, clipH)\n        }\n\n    }\n    Box(\n        modifier = Modifier\n            .width(widthDp)\n            .height(heightDp)\n            .graphicsLayer {\n                this.transformOrigin = TransformOrigin(0f, 0f)\n                this.alpha = alpha\n                this.scaleX = scale\n                this.scaleY = scale\n                this.clip = true\n                this.shape = object : Shape {\n                    override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density) =\n                        Outline.Rectangle(Rect(clipSize.width, clipSize.height, size.width - clipSize.width, size.height - clipSize.height))\n\n                    override fun toString(): String = \"PhotoTransformShape\"\n                }\n                this.translationX = translateX - clipSize.width * scale\n                this.translationY = translateY - clipSize.height * scale\n\n            }\n    ) {\n        content(scale)\n    }\n}\n\ninternal class GestureNestScrollConnection : NestedScrollConnection {\n\n    var isIntercepted: Boolean = false\n    var canConsumeEvent: Boolean = false\n\n    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {\n        if (isIntercepted) {\n            return available\n        }\n        return super.onPreScroll(available, source)\n    }\n\n    override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {\n        if (available.y > 0) {\n            canConsumeEvent = true\n        }\n        return available\n    }\n}\n\nprivate fun calculateImageSize(containerWidth: Dp, containerHeight: Dp, imageRatio: Float, isLongImage: Boolean): Pair<Dp, Dp> {\n    val layoutRatio = containerWidth / containerHeight\n    return when {\n        isLongImage || imageRatio <= 0f -> containerWidth to containerHeight\n        imageRatio >= layoutRatio -> containerWidth to (containerWidth / imageRatio)\n        else -> (containerHeight * imageRatio) to containerHeight\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/Loading.kt",
    "content": "package com.qmuiteam.photo.compose\n\nimport androidx.compose.animation.core.*\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.drawscope.rotate\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun QMUIPhotoLoading(\n    size: Dp = 32.dp,\n    duration: Int = 600,\n    lineCount: Int = 12,\n    lineColor: Color = Color.LightGray,\n){\n    val transition = rememberInfiniteTransition()\n    val degree = 360f / lineCount\n    val rotate = transition.animateValue(\n        initialValue = 0,\n        targetValue = lineCount - 1,\n        typeConverter = Int.VectorConverter,\n        animationSpec = infiniteRepeatable(tween(duration, 0, LinearEasing))\n    )\n    Canvas(modifier = Modifier.size(size)) {\n        rotate(rotate.value * degree, center){\n            for (i in 0 until lineCount) {\n                rotate(degree * i, center){\n                    drawLine(\n                        lineColor.copy((i+1) / lineCount.toFloat()),\n                        center + Offset(this.size.width / 4f, 0f),\n                        center + Offset(this.size.width / 2f, 0f),\n                        this.size.width / 16f\n                    )\n                }\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/PhotoClipper.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.photo.compose\n\nimport android.graphics.Bitmap\nimport android.graphics.Matrix\nimport android.graphics.drawable.Drawable\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.BlendMode\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Paint\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.graphics.withSaveLayer\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.dp\nimport androidx.core.graphics.drawable.toBitmap\nimport com.qmuiteam.photo.data.PhotoLoadStatus\nimport com.qmuiteam.photo.data.QMUIPhotoProvider\n\nprivate class ClipperPhotoInfo(\n    var scale: Float = 1f,\n    var rect: Rect? = null,\n    var drawable: Drawable? = null,\n    var clipArea: Rect\n)\n\nval DefaultClipFocusAreaSquareCenter = Rect.Zero\n\n@Composable\nfun QMUIPhotoClipper(\n    photoProvider: QMUIPhotoProvider,\n    maskColor: Color = Color.Black.copy(0.64f),\n    clipFocusArea: Rect = DefaultClipFocusAreaSquareCenter,\n    drawClipFocusArea: DrawScope.(Rect) -> Unit = { area ->\n        drawCircle(\n            Color.Black,\n            radius = area.size.minDimension / 2,\n            center = area.center,\n            blendMode = BlendMode.DstOut\n        )\n    },\n    bitmapClipper: (origin: Bitmap, clipArea: Rect, scale: Float) -> Bitmap? = { origin, clipArea, scale ->\n        val matrix = Matrix()\n        matrix.postScale(scale, scale)\n        Bitmap.createBitmap(\n            origin,\n            clipArea.left.toInt(),\n            clipArea.top.toInt(),\n            clipArea.width.toInt(),\n            clipArea.height.toInt(),\n            matrix,\n            false\n        )\n    },\n    operateContent: @Composable BoxWithConstraintsScope.(doClip: () -> Bitmap?) -> Unit\n) {\n    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {\n        val focusArea = if (clipFocusArea == DefaultClipFocusAreaSquareCenter) {\n            val size = (constraints.maxWidth.coerceAtMost(constraints.maxHeight)).toFloat()\n            val left = (constraints.maxWidth - size) / 2\n            val top = (constraints.maxHeight - size) / 2\n            Rect(left, top, left + size, top + size)\n        } else {\n            clipFocusArea\n        }\n\n        val photoInfo = remember(photoProvider) {\n            ClipperPhotoInfo(clipArea = focusArea)\n        }.apply {\n            clipArea = focusArea\n        }\n\n        val doClip = remember(photoInfo) {\n            val func: () -> Bitmap? = lambda@{\n                val origin = photoInfo.drawable?.toBitmap() ?: return@lambda null\n                val rect = photoInfo.rect ?: return@lambda null\n                val scale = rect.width / origin.width\n                val clipRect = photoInfo.clipArea.translate(Offset(-rect.left, -rect.top))\n                val imageArea = Rect(\n                    clipRect.left / scale,\n                    clipRect.top / scale,\n                    clipRect.right / scale,\n                    clipRect.bottom / scale\n                )\n                bitmapClipper(origin, imageArea, scale)\n            }\n            func\n        }\n\n        QMUIGesturePhoto(\n            containerWidth = maxWidth,\n            containerHeight = maxHeight,\n            imageRatio = photoProvider.ratio(),\n            isLongImage = photoProvider.isLongImage(),\n            shouldTransitionExit = false,\n            panEdgeProtection = focusArea,\n            onBeginPullExit = { false },\n            onTapExit = {}\n        ) { _, scale, rect, onImageRatioEnsured ->\n            photoInfo.scale = scale\n            photoInfo.rect = rect\n            QMUIPhotoContent(photoProvider) {\n                photoInfo.drawable = it\n                if (it.intrinsicWidth > 0 && it.intrinsicHeight > 0) {\n                    onImageRatioEnsured(it.intrinsicWidth.toFloat() / it.intrinsicHeight)\n                }\n            }\n        }\n        Canvas(modifier = Modifier.fillMaxSize()) {\n            drawContext.canvas.withSaveLayer(Rect(Offset.Zero, drawContext.size), Paint()) {\n                drawRect(maskColor)\n                drawClipFocusArea(focusArea)\n            }\n        }\n        operateContent(doClip)\n    }\n}\n\n@Composable\nfun BoxScope.QMUIPhotoContent(\n    photoProvider: QMUIPhotoProvider,\n    onSuccess: (Drawable) -> Unit\n) {\n    var loadStatus by remember {\n        mutableStateOf(PhotoLoadStatus.loading)\n    }\n    val photo = remember(photoProvider) {\n        photoProvider.photo()\n    }\n    photo?.Compose(\n        contentScale = ContentScale.Fit,\n        isContainerDimenExactly = true,\n        onSuccess = {\n            loadStatus = PhotoLoadStatus.success\n            onSuccess.invoke(it.drawable)\n        },\n        onError = {\n            loadStatus = PhotoLoadStatus.failed\n        })\n\n    if (loadStatus == PhotoLoadStatus.loading) {\n        Box(modifier = Modifier.align(Alignment.Center)) {\n            QMUIPhotoLoading(size = 48.dp)\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/PhotoConfig.kt",
    "content": "package com.qmuiteam.photo.compose\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.staticCompositionLocalOf\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\n\nclass QMUIPhotoConfig(\n    val blankColor: Color = Color.LightGray\n)\n\nval qmuiPhotoDefaultConfig by lazy { QMUIPhotoConfig() }\nval QMUILocalPhotoConfig = staticCompositionLocalOf { qmuiPhotoDefaultConfig }\n\n\n@Composable\nfun QMUIDefaultPhotoConfigProvider(content: @Composable () -> Unit) {\n    CompositionLocalProvider(QMUILocalPhotoConfig provides qmuiPhotoDefaultConfig) {\n        content()\n    }\n}\n\n\n@Composable\nfun BlankBox() {\n    val blankColor = QMUILocalPhotoConfig.current.blankColor\n    if (blankColor != Color.Transparent) {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(blankColor)\n        )\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/Thumbnail.kt",
    "content": "package com.qmuiteam.photo.compose\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.layout.onGloballyPositioned\nimport androidx.compose.ui.layout.positionInWindow\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.IntSize\nimport androidx.compose.ui.unit.dp\nimport com.qmuiteam.photo.activity.QMUIPhotoViewerActivity\nimport com.qmuiteam.photo.data.*\nimport com.qmuiteam.photo.util.getWindowSize\nimport kotlinx.coroutines.launch\n\nconst val SINGLE_HIGH_IMAGE_MINI_SCREEN_HEIGHT_RATIO = -1F\n\nclass QMUIPhotoThumbnailConfig(\n    val singleSquireImageWidthRatio: Float = 0.5f,\n    val singleWideImageMaxWidthRatio: Float = 0.667f,\n    val singleHighImageDefaultWidthRatio: Float = 0.5f,\n    val singleHighImageMiniHeightRatio: Float = SINGLE_HIGH_IMAGE_MINI_SCREEN_HEIGHT_RATIO,\n    val singleLongImageWidthRatio: Float = 0.5f,\n    val averageIfTwoImage: Boolean = true,\n    val horGap: Dp = 5.dp,\n    val verGap: Dp = 5.dp,\n    val alphaWhenPressed: Float = 1f\n)\n\nval qmuiDefaultPhotoThumbnailConfig = QMUIPhotoThumbnailConfig()\n\n@Composable\nprivate fun QMUIPhotoThumbnailItem(\n    thumb: QMUIPhoto?,\n    width: Dp,\n    height: Dp,\n    alphaWhenPressed: Float,\n    isContainerDimenExactly: Boolean,\n    onLayout: (offset: Offset, size: IntSize) -> Unit,\n    onPhotoLoaded: (PhotoResult) -> Unit,\n    click: (() -> Unit)?,\n) {\n    val interactionSource = remember { MutableInteractionSource() }\n    val isPressed = interactionSource.collectIsPressedAsState()\n    Box(modifier = Modifier\n        .width(width)\n        .height(height)\n        .let {\n\n            if (click != null) {\n                it\n                    .clickable(interactionSource, null) {\n                        click.invoke()\n                    }\n                    .alpha(if (isPressed.value) alphaWhenPressed else 1f)\n            } else {\n                it\n            }\n        }\n        .onGloballyPositioned {\n            onLayout(it.positionInWindow(), it.size)\n        }\n    ) {\n        thumb?.Compose(\n            contentScale = if (isContainerDimenExactly) ContentScale.Crop else ContentScale.Fit,\n            isContainerDimenExactly = isContainerDimenExactly,\n            onSuccess = {\n                onPhotoLoaded(it)\n            },\n            onError = null\n        )\n    }\n}\n\n\n@Composable\nfun QMUIPhotoThumbnailWithViewer(\n    targetActivity: Class<out QMUIPhotoViewerActivity> = QMUIPhotoViewerActivity::class.java,\n    activity: ComponentActivity,\n    images: List<QMUIPhotoProvider>,\n    config: QMUIPhotoThumbnailConfig = remember { qmuiDefaultPhotoThumbnailConfig }\n) {\n    QMUIPhotoThumbnail(images, config) { list, index ->\n        val intent = QMUIPhotoViewerActivity.intentOf(activity, targetActivity, list, index)\n        activity.startActivity(intent)\n        activity.overridePendingTransition(0, 0)\n    }\n}\n\n@Composable\nfun QMUIPhotoThumbnail(\n    images: List<QMUIPhotoProvider>,\n    config: QMUIPhotoThumbnailConfig = remember { qmuiDefaultPhotoThumbnailConfig },\n    onClick: ((images: List<QMUIPhotoTransitionInfo>, index: Int) -> Unit)? = null\n) {\n    if (images.size < 0) {\n        return\n    }\n    val renderInfo = remember(images) {\n        Array(images.size) {\n            QMUIPhotoTransitionInfo(images[it], null, null, null)\n        }\n    }\n    val context = LocalContext.current\n    BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {\n        if (images.size == 1) {\n            val image = images[0]\n            val thumb = remember(image) {\n                image.thumbnail(true)\n            }\n            if (thumb != null) {\n                val ratio = image.ratio()\n                when {\n                    ratio <= 0 -> {\n                        QMUIPhotoThumbnailItem(\n                            thumb,\n                            Dp.Unspecified,\n                            Dp.Unspecified,\n                            config.alphaWhenPressed,\n                            isContainerDimenExactly = false,\n                            onLayout = { offset, size ->\n                                renderInfo[0].offsetInWindow = offset\n                                renderInfo[0].size = size\n                            },\n                            onPhotoLoaded = {\n                                renderInfo[0].photo = it.drawable\n                            },\n                            click = if (onClick != null) {\n                                {\n                                    onClick.invoke(renderInfo.toList(), 0)\n                                }\n                            } else null\n                        )\n                    }\n                    ratio == 1f -> {\n                        val wh = maxWidth * config.singleSquireImageWidthRatio\n                        QMUIPhotoThumbnailItem(\n                            thumb,\n                            wh,\n                            wh,\n                            config.alphaWhenPressed,\n                            isContainerDimenExactly = true,\n                            onLayout = { offset, size ->\n                                renderInfo[0].offsetInWindow = offset\n                                renderInfo[0].size = size\n                            },\n                            onPhotoLoaded = {\n                                renderInfo[0].photo = it.drawable\n                            },\n                            click = if (onClick != null) {\n                                {\n                                    onClick.invoke(renderInfo.toList(), 0)\n                                }\n                            } else null\n                        )\n                    }\n                    ratio > 1f -> {\n                        val width = maxWidth * config.singleWideImageMaxWidthRatio\n                        val height = width / ratio\n                        QMUIPhotoThumbnailItem(\n                            thumb,\n                            width,\n                            height,\n                            config.alphaWhenPressed,\n                            isContainerDimenExactly = true,\n                            onLayout = { offset, size ->\n                                renderInfo[0].offsetInWindow = offset\n                                renderInfo[0].size = size\n                            },\n                            onPhotoLoaded = {\n                                renderInfo[0].photo = it.drawable\n                            },\n                            click = if (onClick != null) {\n                                {\n                                    onClick.invoke(renderInfo.toList(), 0)\n                                }\n                            } else null\n                        )\n                    }\n                    image.isLongImage() -> {\n                        val width = maxWidth * config.singleLongImageWidthRatio\n                        val heightRatio = if (config.singleHighImageMiniHeightRatio == SINGLE_HIGH_IMAGE_MINI_SCREEN_HEIGHT_RATIO) {\n                            val windowSize = getWindowSize(context)\n                            windowSize.width * 1f / windowSize.height\n                        } else {\n                            config.singleHighImageMiniHeightRatio\n                        }\n                        val height = width / heightRatio\n                        QMUIPhotoThumbnailItem(\n                            thumb,\n                            width,\n                            height,\n                            config.alphaWhenPressed,\n                            isContainerDimenExactly = true,\n                            onLayout = { offset, size ->\n                                renderInfo[0].offsetInWindow = offset\n                                renderInfo[0].size = size\n                            },\n                            onPhotoLoaded = {\n                                renderInfo[0].photo = it.drawable\n                            },\n                            click = if (onClick != null) {\n                                {\n                                    onClick.invoke(renderInfo.toList(), 0)\n                                }\n                            } else null\n                        )\n                    }\n                    else -> {\n                        var width = maxWidth * config.singleHighImageDefaultWidthRatio\n                        var height = width / ratio\n                        val heightMiniRatio = if (config.singleHighImageMiniHeightRatio == SINGLE_HIGH_IMAGE_MINI_SCREEN_HEIGHT_RATIO) {\n                            val windowSize = getWindowSize(context)\n                            windowSize.width * 1f / windowSize.height\n                        } else {\n                            config.singleHighImageMiniHeightRatio\n                        }\n                        if (ratio < heightMiniRatio) {\n                            height = width * heightMiniRatio\n                            width = height * ratio\n                        }\n                        QMUIPhotoThumbnailItem(\n                            thumb,\n                            width,\n                            height,\n                            config.alphaWhenPressed,\n                            isContainerDimenExactly = true,\n                            onLayout = { offset, size ->\n                                renderInfo[0].offsetInWindow = offset\n                                renderInfo[0].size = size\n                            },\n                            onPhotoLoaded = {\n                                renderInfo[0].photo = it.drawable\n                            },\n                            click = if (onClick != null) {\n                                {\n                                    onClick.invoke(renderInfo.toList(), 0)\n                                }\n                            } else null\n                        )\n                    }\n                }\n            }\n        } else if (images.size == 2 && config.averageIfTwoImage) {\n            RowImages(images, renderInfo, config, maxWidth, 2, 0, onClick)\n        } else {\n            Column(modifier = Modifier.fillMaxWidth()) {\n                for (i in 0 until (images.size / 3 + if (images.size % 3 > 0) 1 else 0).coerceAtMost(\n                    3\n                )) {\n                    if (i > 0) {\n                        Spacer(modifier = Modifier.height(config.verGap))\n                    }\n                    RowImages(\n                        images,\n                        renderInfo,\n                        config,\n                        this@BoxWithConstraints.maxWidth,\n                        3,\n                        i * 3,\n                        onClick\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\nfun RowImages(\n    images: List<QMUIPhotoProvider>,\n    renderInfo: Array<QMUIPhotoTransitionInfo>,\n    config: QMUIPhotoThumbnailConfig,\n    containerWidth: Dp,\n    rowCount: Int,\n    startIndex: Int,\n    onClick: ((images: List<QMUIPhotoTransitionInfo>, index: Int) -> Unit)?\n) {\n    val wh = (containerWidth - config.horGap * (rowCount - 1)) / rowCount\n    Row(\n        modifier = Modifier\n            .fillMaxWidth()\n            .height(wh)\n    ) {\n        for (i in startIndex until (startIndex + rowCount).coerceAtMost(images.size)) {\n            if (i != startIndex) {\n                Spacer(modifier = Modifier.width(config.horGap))\n            }\n            val image = images[i]\n            QMUIPhotoThumbnailItem(\n                remember(image) {\n                    image.thumbnail(true)\n                },\n                wh,\n                wh,\n                config.alphaWhenPressed,\n                isContainerDimenExactly = true,\n                onLayout = { offset, size ->\n                    renderInfo[i].offsetInWindow = offset\n                    renderInfo[i].size = size\n                },\n                onPhotoLoaded = {\n                    renderInfo[i].photo = it.drawable\n                },\n                click = if (onClick != null) {\n                    {\n                        onClick.invoke(renderInfo.toList(), i)\n                    }\n                } else null\n            )\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Buckets.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.animation.*\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material.Text\nimport androidx.compose.material.ripple.rememberRipple\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.drawBehind\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.constraintlayout.compose.*\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.ex.drawBottomSeparator\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\nimport com.qmuiteam.compose.core.ui.QMUIMarkIcon\nimport com.qmuiteam.photo.data.QMUIMediaPhotoBucketVO\n\n@Composable\nfun ConstraintLayoutScope.QMUIPhotoBucketChooser(\n    focus: Boolean,\n    data: List<QMUIMediaPhotoBucketVO>,\n    currentId: String,\n    onBucketClick: (QMUIMediaPhotoBucketVO) -> Unit,\n    onDismiss: () -> Unit\n) {\n    val (mask, content) = createRefs()\n    AnimatedVisibility(\n        visible = focus,\n        modifier = Modifier.constrainAs(mask) {\n            width = Dimension.fillToConstraints\n            height = Dimension.fillToConstraints\n            start.linkTo(parent.start)\n            end.linkTo(parent.end)\n            top.linkTo(parent.top)\n            bottom.linkTo(parent.bottom)\n        },\n        enter = fadeIn(),\n        exit = fadeOut()\n    ) {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(QMUILocalPickerConfig.current.bucketChooserMaskColor)\n                .clickable(\n                    interactionSource = remember { MutableInteractionSource() },\n                    indication = null\n                ) {\n                    onDismiss()\n                }\n        )\n    }\n    AnimatedVisibility(\n        visible = focus,\n        modifier = Modifier.constrainAs(content) {\n            width = Dimension.fillToConstraints\n            height = Dimension.fillToConstraints\n            start.linkTo(parent.start)\n            end.linkTo(parent.end)\n            top.linkTo(parent.top)\n            bottom.linkTo(parent.bottom)\n        },\n        enter = slideInVertically(initialOffsetY = { -it }),\n        exit = slideOutVertically(targetOffsetY = { -it })\n    ) {\n        BoxWithConstraints() {\n            val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n                WindowInsetsCompat.Type.navigationBars()\n            ).dp()\n            LazyColumn(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .heightIn(max = (maxHeight - insets.bottom) * 0.8f)\n                    .wrapContentHeight()\n                    .background(QMUILocalPickerConfig.current.bucketChooserBgColor),\n            ) {\n                items(data, key = { it.id }) {\n                    QMUIPhotoBucketItem(it, it.id == currentId, onBucketClick)\n                }\n            }\n        }\n\n    }\n}\n\n@Composable\nfun QMUIPhotoBucketItem(\n    data: QMUIMediaPhotoBucketVO,\n    isCurrent: Boolean,\n    onBucketClick: (QMUIMediaPhotoBucketVO) -> Unit\n) {\n    val h = 60.dp\n    val textBeginMargin = 16.dp\n    val config = QMUILocalPickerConfig.current\n    ConstraintLayout(modifier = Modifier\n        .fillMaxWidth()\n        .height(h)\n        .drawBehind {\n            drawBottomSeparator(insetStart = h + textBeginMargin, color = config.commonSeparatorColor)\n        }\n        .clickable(\n            interactionSource = remember { MutableInteractionSource() },\n            indication = rememberRipple(color = config.bucketChooserIndicationColor)\n        ) {\n            onBucketClick(data)\n        }\n    ) {\n        val (pic, title, num, mark) = createRefs()\n        val chainHor = createHorizontalChain(title, num, chainStyle = ChainStyle.Packed(0f))\n        constrain(chainHor) {\n            start.linkTo(pic.end, margin = textBeginMargin)\n            end.linkTo(mark.start, margin = 16.dp)\n        }\n        Box(modifier = Modifier\n            .size(h)\n            .constrainAs(pic) {\n                start.linkTo(parent.start)\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n            }) {\n            val thumbnail = remember(data) {\n                data.list.firstOrNull()?.photoProvider?.thumbnail(true)\n            }\n            thumbnail?.Compose(\n                contentScale = ContentScale.Crop,\n                isContainerDimenExactly = true,\n                onSuccess = null,\n                onError = null\n            )\n        }\n        Text(\n            text = data.name,\n            fontSize = 17.sp,\n            color = config.bucketChooserMainTextColor,\n            maxLines = 1,\n            overflow = TextOverflow.Ellipsis,\n            modifier = Modifier.constrainAs(title) {\n                width = Dimension.preferredWrapContent\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n            }\n        )\n        Text(\n            text = \"(${data.list.size})\",\n            fontSize = 17.sp,\n            color = config.bucketChooserCountTextColor,\n            modifier = Modifier.constrainAs(num) {\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n            }\n        )\n        QMUIMarkIcon(\n            modifier = Modifier.constrainAs(mark) {\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n                end.linkTo(parent.end, 16.dp)\n                visibility = if (isCurrent) Visibility.Visible else Visibility.Gone\n            },\n            tint = config.commonIconCheckedTintColor\n        )\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Common.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.animation.*\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.ColorFilter\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.qmuiteam.compose.core.ui.CheckStatus\nimport com.qmuiteam.compose.core.ui.PressWithAlphaBox\nimport com.qmuiteam.compose.core.ui.QMUICheckBox\nimport kotlinx.coroutines.flow.StateFlow\n\n@OptIn(ExperimentalAnimationApi::class)\n@Composable\nfun QMUIPhotoPickCheckBox(pickIndex: Int) {\n    val config = QMUILocalPickerConfig.current\n    val strokeWidth = with(LocalDensity.current) {\n        2.dp.toPx()\n    }\n    AnimatedVisibility(\n        visible = pickIndex < 0,\n        enter = fadeIn(),\n        exit = fadeOut()\n    ) {\n        Canvas(modifier = Modifier.fillMaxSize()) {\n            drawCircle(\n                color = config.commonIconNormalTintColor,\n                radius = (size.minDimension - strokeWidth) / 2.0f,\n                style = Stroke(strokeWidth)\n            )\n        }\n    }\n    AnimatedVisibility(\n        visible = pickIndex >= 0,\n        enter = fadeIn(),\n        exit = fadeOut()\n    ) {\n        Box(\n            modifier = Modifier\n                .fillMaxSize()\n                .clip(CircleShape)\n                .background(config.commonIconCheckedTintColor),\n            contentAlignment = Alignment.Center\n        ) {\n            if (transition.targetState != EnterExitState.PostExit) {\n                Text(\n                    text = \"${pickIndex + 1}\",\n                    color = config.commonIconCheckedTextColor,\n                    fontSize = 12.sp\n                )\n            }\n        }\n    }\n}\n\n@Composable\nfun QMUIPhotoPickRadio(\n    checked: Boolean,\n    ratioSize: Dp = 18.dp,\n    strokeWidthDp: Dp = 1.6.dp\n) {\n    Box(modifier = Modifier.size(ratioSize)) {\n        val strokeWidth = with(LocalDensity.current) {\n            strokeWidthDp.toPx()\n        }\n        val config = QMUILocalPickerConfig.current\n        AnimatedVisibility(\n            visible = !checked,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            Canvas(modifier = Modifier.size(ratioSize)) {\n                drawCircle(\n                    color = config.commonIconNormalTintColor,\n                    radius = (size.minDimension - strokeWidth) / 2.0f,\n                    style = Stroke(strokeWidth)\n                )\n            }\n        }\n        AnimatedVisibility(\n            visible = checked,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            Canvas(modifier = Modifier.size(ratioSize)) {\n                drawCircle(\n                    color = config.commonIconCheckedTintColor,\n                    radius = (size.minDimension - strokeWidth) / 2.0f,\n                    style = Stroke(strokeWidth)\n                )\n\n                drawCircle(\n                    color = config.commonIconCheckedTintColor,\n                    radius = (size.minDimension - strokeWidth * 4) / 2.0f,\n                )\n            }\n        }\n    }\n}\n\n@Composable\nfun OriginOpenButton(\n    modifier: Modifier = Modifier,\n    isOriginOpenFlow: StateFlow<Boolean>,\n    onToggleOrigin: (toOpen: Boolean) -> Unit,\n) {\n    val isOriginOpen by isOriginOpenFlow.collectAsState()\n    Row(\n        modifier = modifier.clickable(\n            interactionSource = remember {\n                MutableInteractionSource()\n            },\n            indication = null\n        ) {\n            onToggleOrigin.invoke(!isOriginOpen)\n        },\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.Absolute.spacedBy(5.dp)\n    ) {\n        QMUIPhotoPickRadio(isOriginOpen)\n        Text(\n            \"原图\",\n            fontSize = 17.sp,\n            color = QMUILocalPickerConfig.current.commonTextButtonTextColor\n        )\n    }\n}\n\n@Composable\nfun PickCurrentCheckButton(\n    modifier: Modifier = Modifier,\n    isPicked: Boolean,\n    onPicked: (toPick: Boolean) -> Unit,\n) {\n    val config = QMUILocalPickerConfig.current\n    Row(\n        modifier = modifier.clickable(\n            interactionSource = remember {\n                MutableInteractionSource()\n            },\n            indication = null\n        ) {\n            onPicked.invoke(!isPicked)\n        },\n        verticalAlignment = Alignment.CenterVertically,\n        horizontalArrangement = Arrangement.Absolute.spacedBy(5.dp)\n    ) {\n        QMUICheckBox(\n            size = 18.dp,\n            status = if (isPicked) CheckStatus.checked else CheckStatus.none,\n            tint = if (isPicked) config.commonIconCheckedTintColor else config.commonIconNormalTintColor,\n            background = if (isPicked) config.commonIconNormalTintColor else Color.Transparent,\n        )\n        Text(\n            \"选择\",\n            fontSize = 17.sp,\n            color = QMUILocalPickerConfig.current.commonTextButtonTextColor\n        )\n    }\n}\n\n\n@Composable\ninternal fun CommonTextButton(\n    modifier: Modifier,\n    enable: Boolean,\n    text: String,\n    onClick: () -> Unit\n) {\n    PressWithAlphaBox(\n        enable = enable,\n        modifier = Modifier\n            .fillMaxHeight()\n            .padding(horizontal = 16.dp)\n            .then(modifier),\n        onClick = {\n            onClick()\n        }\n    ) {\n        Text(\n            text,\n            fontSize = 17.sp,\n            color = QMUILocalPickerConfig.current.commonTextButtonTextColor,\n            modifier = Modifier.align(Alignment.Center)\n        )\n    }\n}\n\n@Composable\ninternal fun CommonImageButton(\n    modifier: Modifier = Modifier,\n    res: Int,\n    enabled: Boolean = true,\n    checked: Boolean = false,\n    onClick: () -> Unit\n){\n    PressWithAlphaBox(\n        modifier = modifier,\n        enable = enabled,\n        onClick = {\n            onClick()\n        }\n    ) {\n        val config = QMUILocalPickerConfig.current\n        Image(\n            painter = painterResource(res),\n            contentDescription = \"\",\n            colorFilter = ColorFilter.tint(if(checked) config.commonIconCheckedTintColor else config.commonIconNormalTintColor),\n            contentScale = ContentScale.Inside\n        )\n    }\n}\n\n@Composable\ninternal fun CommonButton(\n    modifier: Modifier = Modifier,\n    enabled: Boolean,\n    text: String,\n    onClick: () -> Unit\n) {\n    val config = QMUILocalPickerConfig.current\n    val interactionSource = remember { MutableInteractionSource() }\n    val isPressed = interactionSource.collectIsPressedAsState()\n    val bgColor = when {\n        !enabled -> config.commonButtonDisableBgColor\n        isPressed.value -> config.commonButtonPressBgColor\n        else -> config.commonButtonNormalBgColor\n    }\n    val textColor = when {\n        !enabled -> config.commonButtonDisabledTextColor\n        isPressed.value -> config.commonButtonPressedTextColor\n        else -> config.commonButtonNormalTextColor\n    }\n    Box(\n        modifier = modifier\n            .clip(RoundedCornerShape(4.dp))\n            .background(bgColor)\n            .clickable(\n                interactionSource = interactionSource,\n                indication = null,\n                enabled = enabled\n            ) {\n                onClick()\n            }\n            .padding(start = 10.dp, end = 10.dp, top = 3.dp, bottom = 4.dp)\n    ) {\n        Text(\n            text = text,\n            fontSize = 17.sp,\n            color = textColor\n        )\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Config.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.staticCompositionLocalOf\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.qmuiteam.compose.core.ui.QMUITopBarItem\nimport com.qmuiteam.compose.core.ui.qmuiPrimaryColor\nimport kotlinx.coroutines.flow.StateFlow\n\nclass QMUIPhotoPickerConfig(\n    val editable: Boolean = true,\n    val primaryColor: Color = qmuiPrimaryColor,\n    val commonTextButtonTextColor: Color = Color.White,\n    val commonSeparatorColor: Color = Color.White.copy(alpha = 0.3f),\n    val commonIconNormalTintColor: Color = Color.White.copy(0.9f),\n    val commonIconCheckedTintColor: Color = primaryColor,\n    val commonIconCheckedTextColor: Color = Color.White.copy(alpha = 0.6f),\n\n    val commonButtonNormalTextColor: Color = Color.White,\n    val commonButtonNormalBgColor: Color = primaryColor,\n    val commonButtonDisabledTextColor: Color = Color.White.copy(alpha = 0.3f),\n    val commonButtonDisableBgColor: Color = Color.White.copy(alpha = 0.15f),\n    val commonButtonPressBgColor: Color = primaryColor.copy(alpha = 0.8f),\n    val commonButtonPressedTextColor: Color = commonButtonNormalTextColor,\n\n    val topBarBgColor: Color = Color(0xFF222222),\n    val toolBarBgColor: Color = topBarBgColor,\n\n    val topBarBucketFactory: (\n        textFlow: StateFlow<String>,\n        isFocusFlow: StateFlow<Boolean>,\n        onClick: () -> Unit\n    ) -> QMUITopBarItem = { textFlow, isFocusFlow, onClick ->\n        QMUIPhotoPickerBucketTopBarItem(\n            bgColor = Color.White.copy(alpha = 0.15f),\n            textColor = Color.White,\n            iconBgColor = Color.White.copy(alpha = 0.72f),\n            iconColor = Color(0xFF333333),\n            textFlow = textFlow,\n            isFocusFlow = isFocusFlow,\n            onClick = onClick\n        )\n    },\n    val topBarSendFactory: (\n        canSendSelf: Boolean,\n        maxSelectCount: Int,\n        selectCountFlow: StateFlow<Int>,\n        onClick: () -> Unit\n    ) -> QMUITopBarItem = { canSendSelf, maxSelectCount, selectCountFlow, onClick ->\n        QMUIPhotoSendTopBarItem(\n            text = \"发送\",\n            canSendSelf = canSendSelf,\n            maxSelectCount = maxSelectCount,\n            selectCountFlow = selectCountFlow,\n            onClick = onClick\n        )\n    },\n\n    val screenBgColor: Color = Color(0xFF333333),\n    val loadingColor: Color = Color.White,\n    val tipTextColor: Color = Color.White,\n\n    val gridPreferredSize: Dp = 80.dp,\n    val gridGap: Dp = 2.dp,\n    val gridBorderColor: Color = Color.White.copy(alpha = 0.15f),\n\n    val bucketChooserMaskColor: Color = Color.Black.copy(alpha = 0.36f),\n    val bucketChooserBgColor: Color = topBarBgColor,\n    val bucketChooserIndicationColor: Color = Color.White.copy(alpha = 0.2f),\n    val bucketChooserMainTextColor: Color = Color.White,\n    val bucketChooserCountTextColor: Color = Color.White.copy(alpha = 0.64f),\n\n    val editPaintOptions: List<EditPaint> = listOf(\n        MosaicEditPaint(16),\n        MosaicEditPaint(50),\n        ColorEditPaint(Color.White),\n        ColorEditPaint(Color.Black),\n        ColorEditPaint(Color.Red),\n        ColorEditPaint(Color.Yellow),\n        ColorEditPaint(Color.Green),\n        ColorEditPaint(Color.Blue),\n        ColorEditPaint(Color.Magenta)\n    ),\n    val graffitiPaintStrokeWidth: Dp = 5.dp,\n    val mosaicPaintStrokeWidth: Dp = 20.dp,\n\n    val textEditMaskColor:Color = Color.Black.copy(0.5f),\n    val textEditColorOptions: List<ColorEditPaint> = listOf(\n        ColorEditPaint(Color.White),\n        ColorEditPaint(Color.Black),\n        ColorEditPaint(Color.Red),\n        ColorEditPaint(Color.Yellow),\n        ColorEditPaint(Color.Green),\n        ColorEditPaint(Color.Blue),\n        ColorEditPaint(Color.Magenta)\n    ),\n    val textEditFontSize: TextUnit = 30.sp,\n    val textEditLineSpace: TextUnit = 3.sp,\n    val textCursorColor: Color = primaryColor,\n\n    val editLayerDeleteAreaNormalBgColor: Color = Color.Black.copy(alpha = 0.3f),\n    val editLayerDeleteAreaNormalFocusColor: Color = Color.Red.copy(alpha = 0.6f),\n)\n\nval qmuiPhotoPickerDefaultConfig by lazy { QMUIPhotoPickerConfig() }\nval QMUILocalPickerConfig = staticCompositionLocalOf { qmuiPhotoPickerDefaultConfig }\n\n@Composable\nfun QMUIDefaultPickerConfigProvider(content: @Composable () -> Unit) {\n    CompositionLocalProvider(QMUILocalPickerConfig provides qmuiPhotoPickerDefaultConfig) {\n        content()\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Edit.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport android.graphics.drawable.Drawable\nimport androidx.activity.OnBackPressedCallback\nimport androidx.activity.OnBackPressedDispatcher\nimport androidx.compose.animation.AnimatedVisibility\nimport androidx.compose.animation.fadeIn\nimport androidx.compose.animation.fadeOut\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.gestures.awaitFirstDown\nimport androidx.compose.foundation.gestures.forEachGesture\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.foundation.text.BasicTextField\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.focus.FocusRequester\nimport androidx.compose.ui.focus.focusRequester\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.*\nimport androidx.compose.ui.input.pointer.consumeDownChange\nimport androidx.compose.ui.input.pointer.consumePositionChange\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.text.TextRange\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.Constraints\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.constraintlayout.compose.ChainStyle\nimport androidx.constraintlayout.compose.ConstraintLayout\nimport androidx.constraintlayout.compose.Dimension\nimport androidx.constraintlayout.compose.Visibility\nimport androidx.core.graphics.drawable.toBitmap\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.R\nimport com.qmuiteam.compose.core.helper.OnePx\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\nimport com.qmuiteam.photo.compose.QMUIGesturePhoto\nimport com.qmuiteam.photo.data.QMUIMediaPhotoVO\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.flow.MutableStateFlow\n\nprivate sealed class PickerEditScene\n\nprivate object PickerEditSceneNormal : PickerEditScene()\nprivate object PickerEditScenePaint : PickerEditScene()\nprivate class PickerEditSceneText(val editLayer: TextEditLayer? = null) : PickerEditScene()\nprivate class PickerEditSceneClip(val area: Rect) : PickerEditScene()\n\nprivate class EditSceneHolder<T : PickerEditScene>(var scene: T? = null)\n\nprivate class MutablePickerPhotoInfo(\n    var drawable: Drawable?,\n    var mosaicBitmapCache: MutableMap<Int, ImageBitmap> = mutableMapOf()\n)\n\ninternal data class PickerPhotoLayoutInfo(val scale: Float, val rect: Rect)\n\n\n@Composable\nfun QMUIPhotoPickerEdit(\n    onBackPressedDispatcher: OnBackPressedDispatcher,\n    data: QMUIMediaPhotoVO,\n    onBack: () -> Unit,\n) {\n    val sceneState = remember(data) {\n        mutableStateOf<PickerEditScene>(PickerEditSceneNormal)\n    }\n    val scene = sceneState.value\n    val photoInfo = remember(data) {\n        MutablePickerPhotoInfo(null)\n    }\n\n    var photoLayoutInfo by remember(data) {\n        mutableStateOf(PickerPhotoLayoutInfo(1f, Rect.Zero))\n    }\n\n    val paintEditLayers = remember(data) {\n        mutableStateListOf<PaintEditLayer>()\n    }\n\n    val textEditLayers = remember(data) {\n        mutableStateListOf<TextEditLayer>()\n    }\n\n    val config = QMUILocalPickerConfig.current\n\n    var forceHideTools by remember {\n        mutableStateOf(false)\n    }\n\n    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {\n        QMUIGesturePhoto(\n            containerWidth = maxWidth,\n            containerHeight = maxHeight,\n            imageRatio = data.model.ratio(),\n            shouldTransitionEnter = false,\n            shouldTransitionExit = false,\n            isLongImage = data.photoProvider.isLongImage(),\n            onBeginPullExit = {\n                false\n            },\n            onTapExit = {\n\n            },\n            onPress = {\n                textEditLayers.forEach {\n                    it.isFocusFlow.value = false\n                }\n            }\n        ) { _, scale, rect, onImageRatioEnsured ->\n            photoLayoutInfo = PickerPhotoLayoutInfo(scale, rect)\n            QMUIPhotoPickerEditPhotoContent(data) {\n                photoInfo.drawable = it\n                onImageRatioEnsured(it.intrinsicWidth.toFloat() / it.intrinsicHeight)\n            }\n        }\n\n        QMUIPhotoEditHistoryList(\n            photoLayoutInfo,\n            paintEditLayers,\n            textEditLayers,\n            onFocusLayer = { focusLayer ->\n                textEditLayers.forEach {\n                    if (it != focusLayer) {\n                        it.isFocusFlow.value = false\n                    }\n                }\n            },\n            onEditTextLayer = {\n                sceneState.value = PickerEditSceneText(it)\n            },\n            onDeleteTextLayer = {\n                textEditLayers.remove(it)\n            },\n            onToggleDragging = {\n                forceHideTools = it\n            }\n        )\n\n        AnimatedVisibility(\n            visible = scene == PickerEditSceneNormal || scene == PickerEditScenePaint,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUIPhotoPickerEditPaintScreen(\n                paintState = scene == PickerEditScenePaint,\n                photoInfo = photoInfo,\n                editLayers = paintEditLayers,\n                layoutInfo = photoLayoutInfo,\n                forceHideTools = forceHideTools,\n                onBack = onBack,\n                onPaintClick = {\n                    sceneState.value = if (it) PickerEditScenePaint else PickerEditSceneNormal\n                },\n                onTextClick = {\n                    sceneState.value = PickerEditSceneText()\n                },\n                onClipClick = {\n                    sceneState.value = PickerEditSceneClip(Rect(Offset.Zero, photoLayoutInfo.rect.size))\n                },\n                onFinishPaintLayer = {\n                    paintEditLayers.add(it)\n                },\n                onEnsureClick = {\n\n                },\n                onRevoke = {\n                    paintEditLayers.removeLastOrNull()\n                }\n            )\n        }\n\n        AnimatedVisibility(\n            visible = scene is PickerEditSceneText,\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            // For exit animation\n            val sceneHolder = remember {\n                EditSceneHolder(scene as? PickerEditSceneText)\n            }\n            if (scene is PickerEditSceneText) {\n                sceneHolder.scene = scene\n            }\n            val textScene = sceneHolder.scene\n            if (textScene != null) {\n                QMUIPhotoPickerEditTextScreen(\n                    onBackPressedDispatcher,\n                    photoLayoutInfo,\n                    constraints,\n                    textScene.editLayer,\n                    textScene.editLayer?.color ?: config.textEditColorOptions[0].color,\n                    textScene.editLayer?.reverse ?: false,\n                    onCancel = {\n                        sceneState.value = PickerEditSceneNormal\n                    },\n                    onFinishTextLayer = { toReplace, target ->\n                        if (toReplace != null) {\n                            val index = textEditLayers.indexOf(toReplace)\n                            if (index >= 0) {\n                                textEditLayers[index] = target\n                            } else {\n                                textEditLayers.add(target)\n                            }\n                        } else {\n                            textEditLayers.add(target)\n                        }\n                        sceneState.value = PickerEditSceneNormal\n                    }\n                )\n            }\n\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerEditPaintScreen(\n    paintState: Boolean,\n    editLayers: List<PaintEditLayer>,\n    photoInfo: MutablePickerPhotoInfo,\n    layoutInfo: PickerPhotoLayoutInfo,\n    forceHideTools: Boolean,\n    onBack: () -> Unit,\n    onPaintClick: (toPaint: Boolean) -> Unit,\n    onTextClick: () -> Unit,\n    onClipClick: () -> Unit,\n    onFinishPaintLayer: (PaintEditLayer) -> Unit,\n    onEnsureClick: () -> Unit,\n    onRevoke: () -> Unit\n) {\n    val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n        WindowInsetsCompat.Type.navigationBars() or\n                WindowInsetsCompat.Type.statusBars() or\n                WindowInsetsCompat.Type.displayCutout()\n    ).dp()\n\n    val paintEditOptions = QMUILocalPickerConfig.current.editPaintOptions\n    var paintEditCurrentIndex by remember {\n        mutableStateOf(4)\n    }\n\n    if (paintEditCurrentIndex >= paintEditOptions.size) {\n        paintEditCurrentIndex = paintEditOptions.size - 1\n    }\n\n    var showTools by remember {\n        mutableStateOf(true)\n    }\n\n    ConstraintLayout(modifier = Modifier.fillMaxSize()) {\n\n        Box(modifier = Modifier\n            .fillMaxSize()\n            .constrainAs(createRef()) {\n                width = Dimension.fillToConstraints\n                height = Dimension.fillToConstraints\n                visibility = if (paintState) Visibility.Visible else Visibility.Gone\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n            }) {\n            QMUIPhotoPaintCanvas(\n                paintEditOptions[paintEditCurrentIndex],\n                photoInfo,\n                layoutInfo,\n                editLayers,\n                onTouchBegin = {\n                    showTools = false\n                },\n                onTouchEnd = {\n                    showTools = true\n                    onFinishPaintLayer(it)\n                }\n            )\n        }\n\n        AnimatedVisibility(\n            visible = showTools && !forceHideTools,\n            modifier = Modifier.constrainAs(createRef()) {\n                width = Dimension.fillToConstraints\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                top.linkTo(parent.top)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            Box(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .height(insets.top + 60.dp)\n                    .background(brush = Brush.verticalGradient(listOf(Color.Black.copy(0.2f), Color.Transparent)))\n            )\n        }\n\n        AnimatedVisibility(\n            visible = showTools && !forceHideTools,\n            modifier = Modifier.constrainAs(createRef()) {\n                width = Dimension.fillToConstraints\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                bottom.linkTo(parent.bottom)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            Box(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .height(insets.bottom + 150.dp)\n                    .background(brush = Brush.verticalGradient(listOf(Color.Transparent, Color.Black.copy(0.2f))))\n            )\n        }\n\n        AnimatedVisibility(\n            visible = showTools && !forceHideTools,\n            modifier = Modifier.constrainAs(createRef()) {\n                start.linkTo(parent.start)\n                top.linkTo(parent.top)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            CommonImageButton(\n                modifier = Modifier\n                    .padding(start = insets.left + 16.dp, top = insets.top + 16.dp, end = 16.dp, bottom = 16.dp),\n                res = R.drawable.ic_qmui_topbar_back\n            ) {\n                onBack()\n            }\n        }\n\n        val (toolBar, paintChooser) = createRefs()\n\n        AnimatedVisibility(\n            visible = showTools && !forceHideTools,\n            modifier = Modifier.constrainAs(toolBar) {\n                width = Dimension.fillToConstraints\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                bottom.linkTo(parent.bottom)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUIPhotoPickerEditToolBar(\n                modifier = Modifier.padding(bottom = insets.bottom, start = insets.left, end = insets.right),\n                isPaintState = paintState,\n                onPaintClick = onPaintClick,\n                onTextClick = onTextClick,\n                onClipClick = onClipClick,\n                onEnsureClick = onEnsureClick\n            )\n        }\n\n        AnimatedVisibility(\n            visible = showTools && paintState && !forceHideTools,\n            modifier = Modifier.constrainAs(paintChooser) {\n                width = Dimension.fillToConstraints\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                bottom.linkTo(toolBar.top, 8.dp)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            QMUIPhotoPickerEditPaintOptions(\n                paintEditOptions,\n                24.dp,\n                paintEditCurrentIndex\n            ) {\n                paintEditCurrentIndex = it\n            }\n        }\n\n        AnimatedVisibility(\n            visible = showTools && paintState && !forceHideTools,\n            modifier = Modifier.constrainAs(createRef()) {\n                end.linkTo(parent.end)\n                bottom.linkTo(paintChooser.top)\n            },\n            enter = fadeIn(),\n            exit = fadeOut()\n        ) {\n            CommonImageButton(\n                modifier = Modifier\n                    .padding(start = 16.dp, top = 16.dp, end = insets.right + 16.dp, bottom = 16.dp),\n                res = R.drawable.ic_qmui_topbar_back\n            ) {\n                onRevoke()\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerEditTextScreen(\n    onBackPressedDispatcher: OnBackPressedDispatcher,\n    photoLayoutInfo: PickerPhotoLayoutInfo,\n    constraints: Constraints,\n    editLayer: TextEditLayer?,\n    color: Color,\n    isReverse: Boolean,\n    onCancel: () -> Unit,\n    onFinishTextLayer: (toReplace: TextEditLayer?, target: TextEditLayer) -> Unit\n) {\n    DisposableEffect(\"\") {\n        val callback = object : OnBackPressedCallback(true) {\n            override fun handleOnBackPressed() {\n                onCancel()\n            }\n        }\n        onBackPressedDispatcher.addCallback(callback)\n        object : DisposableEffectResult {\n            override fun dispose() {\n                callback.remove()\n            }\n        }\n    }\n\n    val insets = QMUILocalWindowInsets.current.getInsets(\n        WindowInsetsCompat.Type.navigationBars() or\n                WindowInsetsCompat.Type.ime() or\n                WindowInsetsCompat.Type.statusBars() or\n                WindowInsetsCompat.Type.displayCutout()\n    ).dp()\n\n    var input by remember(editLayer) {\n        val text = editLayer?.text ?: \"\"\n        mutableStateOf(TextFieldValue(text, TextRange(text.length)))\n    }\n\n    val config = QMUILocalPickerConfig.current\n\n    var usedColor by remember(color) {\n        mutableStateOf(color)\n    }\n\n    var usedReverse by remember(isReverse) {\n        mutableStateOf(isReverse)\n    }\n\n    val focusRequester = remember { FocusRequester() }\n\n    LaunchedEffect(Unit) {\n        focusRequester.requestFocus()\n    }\n\n    ConstraintLayout(modifier = Modifier\n        .fillMaxSize()\n        .background(config.textEditMaskColor)\n        .clickable(\n            interactionSource = remember {\n                MutableInteractionSource()\n            },\n            indication = null\n        ) {\n            if (input.text.isNotBlank()) {\n                if (editLayer != null) {\n                    onFinishTextLayer(\n                        editLayer, TextEditLayer(\n                            input.text,\n                            editLayer.fontSize,\n                            editLayer.center,\n                            usedColor,\n                            usedReverse,\n                            editLayer.offsetFlow,\n                            editLayer.scaleFlow,\n                            editLayer.rotationFlow\n                        )\n                    )\n                } else {\n                    onFinishTextLayer(\n                        null, TextEditLayer(\n                            input.text,\n                            config.textEditFontSize,\n                            Offset(\n                                (constraints.maxWidth / 2 - photoLayoutInfo.rect.left) / photoLayoutInfo.scale,\n                                constraints.maxHeight / 2 - photoLayoutInfo.rect.top\n                            ),\n                            usedColor,\n                            usedReverse,\n                            scaleFlow = MutableStateFlow(1 / photoLayoutInfo.scale)\n                        )\n                    )\n                }\n\n            } else {\n                onCancel()\n            }\n        }\n        .padding(insets.left, insets.top, insets.right, insets.bottom)\n    ) {\n        val optionsId = createRef()\n        QMUIPhotoPickerEditTextPaintOptions(\n            config.textEditColorOptions,\n            24.dp,\n            usedColor,\n            isReverse = usedReverse,\n            modifier = Modifier.constrainAs(optionsId) {\n                width = Dimension.fillToConstraints\n                start.linkTo(parent.start)\n                end.linkTo(parent.end)\n                bottom.linkTo(parent.bottom)\n            },\n            onSelect = {\n                usedColor = it\n            },\n            onReverseClick = {\n                usedReverse = it\n            }\n        )\n        BasicTextField(\n            value = input,\n            onValueChange = {\n                input = it\n            },\n            modifier = Modifier\n                .padding(16.dp)\n                .let {\n                    if (usedReverse && input.text.isNotBlank()) {\n                        it.background(color = usedColor, shape = RoundedCornerShape(10.dp))\n                    } else {\n                        it\n                    }\n                }\n                .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 3.dp)\n                .defaultMinSize(8.dp, 48.dp)\n                .width(IntrinsicSize.Min)\n                .focusRequester(focusRequester)\n                .constrainAs(createRef()) {\n                    start.linkTo(parent.start)\n                    end.linkTo(parent.end)\n                    bottom.linkTo(optionsId.top)\n                    top.linkTo(parent.top)\n                },\n            textStyle = TextStyle(\n                color = if (usedReverse) {\n                    if (usedColor == Color.White) Color.Black else Color.White\n                } else usedColor,\n                fontSize = config.textEditFontSize,\n                textAlign = TextAlign.Center,\n                fontWeight = FontWeight.Bold\n            ),\n            cursorBrush = SolidColor(config.textCursorColor)\n        )\n    }\n}\n\n@Composable\nfun QMUIPhotoPickerEditPhotoContent(\n    data: QMUIMediaPhotoVO,\n    onSuccess: (Drawable) -> Unit\n) {\n    Box(modifier = Modifier.fillMaxSize()) {\n        val photo = remember(data) {\n            data.photoProvider.photo()\n        }\n\n        photo?.Compose(\n            contentScale = ContentScale.Fit,\n            isContainerDimenExactly = true,\n            onSuccess = {\n                if (it.drawable.intrinsicWidth > 0 && it.drawable.intrinsicHeight > 0) {\n                    onSuccess(it.drawable)\n                }\n            },\n            onError = null\n        )\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoEditHistoryList(\n    layoutInfo: PickerPhotoLayoutInfo,\n    editLayers: List<PaintEditLayer>,\n    textEditLayers: List<TextEditLayer>,\n    onFocusLayer: (TextEditLayer) -> Unit,\n    onEditTextLayer: (TextEditLayer) -> Unit,\n    onDeleteTextLayer: (TextEditLayer) -> Unit,\n    onToggleDragging: (Boolean) -> Unit\n) {\n    if (layoutInfo.rect == Rect.Zero) {\n        return\n    }\n    val (w, h) = with(LocalDensity.current) {\n        arrayOf(\n            layoutInfo.rect.width.toDp(),\n            layoutInfo.rect.height.toDp()\n        )\n    }\n    Canvas(modifier = Modifier\n        .width(w / layoutInfo.scale)\n        .height(h / layoutInfo.scale)\n        .graphicsLayer {\n            this.transformOrigin = TransformOrigin(0f, 0f)\n            this.translationX = layoutInfo.rect.left\n            this.translationY = layoutInfo.rect.top\n            this.scaleX = layoutInfo.scale\n            this.scaleY = layoutInfo.scale\n            this.clip = true\n        }) {\n        editLayers.forEach {\n            with(it) {\n                draw()\n            }\n        }\n    }\n    textEditLayers.forEach {\n        key(it) {\n            it.Content(\n                layoutInfo = layoutInfo,\n                onFocus = {\n                    onFocusLayer(it)\n                },\n                onToggleDragging = { isDragging ->\n                    onToggleDragging(isDragging)\n                },\n                onEdit = {\n                    onEditTextLayer(it)\n                }) {\n                onDeleteTextLayer(it)\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPaintCanvas(\n    editPaint: EditPaint,\n    photoInfo: MutablePickerPhotoInfo,\n    layoutInfo: PickerPhotoLayoutInfo,\n    editLayers: List<PaintEditLayer>,\n    onTouchBegin: () -> Unit,\n    onTouchEnd: (PaintEditLayer) -> Unit\n) {\n    val drawable = photoInfo.drawable ?: return\n    val (w, h) = with(LocalDensity.current) {\n        arrayOf(\n            layoutInfo.rect.width.toDp(),\n            layoutInfo.rect.height.toDp()\n        )\n    }\n\n    val graffitiStrokeWidth = with(LocalDensity.current) {\n        QMUILocalPickerConfig.current.graffitiPaintStrokeWidth.toPx()\n    }\n    val mosaicStrokeWidth = with(LocalDensity.current) {\n        QMUILocalPickerConfig.current.mosaicPaintStrokeWidth.toPx()\n    }\n    val currentLayerState = remember(editLayers, editPaint, layoutInfo, drawable) {\n        val layer = when (editPaint) {\n            is ColorEditPaint -> {\n                GraffitiEditLayer(Path(), editPaint.color, graffitiStrokeWidth / layoutInfo.scale)\n            }\n            is MosaicEditPaint -> {\n                val image = photoInfo.mosaicBitmapCache[editPaint.scaleLevel] ?: drawable.toBitmap(\n                    drawable.intrinsicWidth / editPaint.scaleLevel,\n                    drawable.intrinsicHeight / editPaint.scaleLevel\n                ).asImageBitmap().also {\n                    photoInfo.mosaicBitmapCache[editPaint.scaleLevel] = it\n                }\n                MosaicEditLayer(\n                    path = Path(),\n                    image = image,\n                    strokeWidth = mosaicStrokeWidth\n                )\n            }\n        }\n        mutableStateOf(layer, neverEqualPolicy())\n    }\n\n    val currentLayer = currentLayerState.value\n\n    Canvas(modifier = Modifier\n        .width(w / layoutInfo.scale)\n        .height(h / layoutInfo.scale)\n        .graphicsLayer {\n            this.transformOrigin = TransformOrigin(0f, 0f)\n            this.translationX = layoutInfo.rect.left\n            this.translationY = layoutInfo.rect.top\n            this.scaleX = layoutInfo.scale\n            this.scaleY = layoutInfo.scale\n            this.clip = true\n        }) {\n        with(currentLayer) {\n            draw()\n        }\n    }\n    Box(\n        modifier = Modifier\n            .fillMaxSize()\n            .pointerInput(editLayers, editPaint, layoutInfo) {\n                coroutineScope {\n                    forEachGesture {\n                        awaitPointerEventScope {\n                            val down = awaitFirstDown(requireUnconsumed = true)\n                            down.consumeDownChange()\n                            currentLayer.path.moveTo(\n                                (down.position.x - layoutInfo.rect.left) / layoutInfo.scale,\n                                (down.position.y - layoutInfo.rect.top) / layoutInfo.scale\n                            )\n                            onTouchBegin()\n                            do {\n                                val event = awaitPointerEvent()\n                                val change = event.changes.find { it.id.value == down.id.value }\n                                if (change != null) {\n                                    change.consumePositionChange()\n                                    currentLayer.path.lineTo(\n                                        (change.position.x - layoutInfo.rect.left) / layoutInfo.scale,\n                                        (change.position.y - layoutInfo.rect.top) / layoutInfo.scale\n                                    )\n                                    currentLayerState.value = currentLayer\n                                }\n\n                            } while (change == null || change.pressed)\n                            onTouchEnd(currentLayer)\n                        }\n                    }\n                }\n            }\n    )\n}\n\n\n@Composable\nprivate fun QMUIPhotoPickerEditPaintOptions(\n    editPaint: List<EditPaint>,\n    size: Dp,\n    selectedIndex: Int,\n    onSelect: (Int) -> Unit\n) {\n    Row(\n        modifier = Modifier\n            .fillMaxWidth()\n            .padding(horizontal = 16.dp),\n        horizontalArrangement = Arrangement.SpaceAround\n    ) {\n        editPaint.forEachIndexed { index, paintEdit ->\n            paintEdit.Compose(size = size, selected = index == selectedIndex) {\n                onSelect(index)\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerEditToolBar(\n    modifier: Modifier,\n    isPaintState: Boolean,\n    onPaintClick: (toPaint: Boolean) -> Unit,\n    onTextClick: () -> Unit,\n    onClipClick: () -> Unit,\n    onEnsureClick: () -> Unit\n) {\n    ConstraintLayout(\n        modifier = modifier\n            .fillMaxWidth()\n            .height(50.dp)\n    ) {\n        val (paint, text, clip, ensure) = createRefs()\n        val horChain = createHorizontalChain(paint, text, clip, chainStyle = ChainStyle.Packed(0f))\n        constrain(horChain) {\n            start.linkTo(parent.start, 16.dp)\n            end.linkTo(ensure.start)\n        }\n        CommonImageButton(\n            modifier = Modifier\n                .padding(10.dp)\n                .constrainAs(paint) {\n                    top.linkTo(parent.top)\n                    bottom.linkTo(parent.bottom)\n                },\n            res = R.drawable.ic_qmui_checkbox_checked,\n            checked = isPaintState\n        ) {\n            onPaintClick(!isPaintState)\n        }\n        CommonImageButton(\n            modifier = Modifier\n                .padding(10.dp)\n                .constrainAs(text) {\n                    top.linkTo(parent.top)\n                    bottom.linkTo(parent.bottom)\n                },\n            res = R.drawable.ic_qmui_checkbox_checked,\n        ) {\n            onTextClick()\n        }\n        CommonImageButton(\n            modifier = Modifier\n                .padding(10.dp)\n                .constrainAs(clip) {\n                    top.linkTo(parent.top)\n                    bottom.linkTo(parent.bottom)\n                },\n            res = R.drawable.ic_qmui_checkbox_checked,\n        ) {\n            onClipClick()\n        }\n        CommonButton(\n            enabled = true,\n            text = \"确定\",\n            onClick = onEnsureClick,\n            modifier = Modifier.constrainAs(ensure) {\n                top.linkTo(parent.top)\n                bottom.linkTo(parent.bottom)\n                end.linkTo(parent.end, 16.dp)\n            }\n        )\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerEditTextPaintOptions(\n    editPaint: List<ColorEditPaint>,\n    size: Dp,\n    color: Color,\n    isReverse: Boolean,\n    modifier: Modifier,\n    onReverseClick: (isReverse: Boolean) -> Unit,\n    onSelect: (Color) -> Unit\n) {\n    Row(\n        modifier = modifier\n            .fillMaxWidth()\n            .padding(horizontal = 16.dp),\n        verticalAlignment = Alignment.CenterVertically\n    ) {\n\n        CommonImageButton(\n            res = R.drawable.ic_qmui_mark,\n            modifier = Modifier.padding(16.dp),\n        ) {\n            onReverseClick(!isReverse)\n        }\n\n        Box(\n            modifier = Modifier\n                .width(OnePx())\n                .height(size + 8.dp)\n                .background(QMUILocalPickerConfig.current.commonSeparatorColor)\n        )\n\n        Row(\n            modifier = Modifier\n                .weight(1f),\n            horizontalArrangement = Arrangement.SpaceAround\n        ) {\n            editPaint.forEach { paintEdit ->\n                paintEdit.Compose(size = size, selected = paintEdit.color == color) {\n                    onSelect(paintEdit.color)\n                }\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Grid.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.LazyListState\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.drawBehind\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.ex.drawTopSeparator\nimport com.qmuiteam.compose.core.helper.OnePx\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\nimport com.qmuiteam.photo.data.QMUIMediaModel\nimport com.qmuiteam.photo.data.QMUIMediaPhotoVO\nimport kotlinx.coroutines.flow.StateFlow\nimport java.lang.StringBuilder\n\nclass QMUIPhotoPickerGridRowData(val key: String, val list: List<QMUIMediaPhotoVO>)\n\nprivate fun convertToRowData(data: List<QMUIMediaPhotoVO>, rowCount: Int): List<QMUIPhotoPickerGridRowData>{\n    val ret = mutableListOf<QMUIPhotoPickerGridRowData>()\n    var list = mutableListOf<QMUIMediaPhotoVO>()\n    val keySb = StringBuilder()\n    data.forEach {\n        keySb.append(it.model.uri)\n        list.add(it)\n        if(list.size == rowCount){\n            ret.add(QMUIPhotoPickerGridRowData(keySb.toString(), list))\n            list = mutableListOf()\n            keySb.clear()\n        }\n    }\n    if(list.isNotEmpty()){\n        ret.add(QMUIPhotoPickerGridRowData(keySb.toString(), list))\n    }\n    return ret\n}\n\n@Composable\nfun QMUIPhotoPickerGrid(\n    data: List<QMUIMediaPhotoVO>,\n    modifier: Modifier = Modifier,\n    state: LazyListState = rememberLazyListState(),\n    pickedItems: List<Long>,\n    onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,\n    onPreview: (model: QMUIMediaModel) -> Unit\n) {\n    BoxWithConstraints(modifier = modifier) {\n        val config = QMUILocalPickerConfig.current\n        val gap = config.gridGap\n        val rowCount = remember(maxWidth, config) {\n            val preferredSize = config.gridPreferredSize\n            ((maxWidth + gap) / (preferredSize + gap)).toInt().coerceAtLeast(2)\n        }\n        val cellSize = remember(maxWidth, gap, rowCount) {\n            ((maxWidth + gap) / rowCount) - gap\n        }\n\n        val rowData = remember(data, rowCount) {\n            convertToRowData(data, rowCount)\n        }\n        // TODO use LazyVerticalGrid for a replacement\n        LazyColumn(\n            state = state,\n            verticalArrangement = Arrangement.Absolute.spacedBy(gap)\n        ) {\n            items(rowData, key = { it.key }){ item ->\n                QMUIPhotoPickerGridRow(item, cellSize, gap, pickedItems, onPickItem, onPreview)\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerGridRow(\n    data: QMUIPhotoPickerGridRowData,\n    cellSize: Dp,\n    gap: Dp,\n    pickedItems: List<Long>,\n    onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,\n    onPreview: (model: QMUIMediaModel) -> Unit\n) {\n    Row(\n        modifier = Modifier\n            .fillMaxWidth(),\n        horizontalArrangement = Arrangement.Absolute.spacedBy(gap),\n    ) {\n        for(i in 0 until data.list.size){\n            QMUIPhotoPickerGridCell(\n                data = data.list[i],\n                cellSize = cellSize,\n                pickedItems = pickedItems,\n                onPickItem = onPickItem,\n                onPreview = onPreview\n            )\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerGridCell(\n    data: QMUIMediaPhotoVO,\n    cellSize: Dp,\n    pickedItems: List<Long>,\n    onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,\n    onPreview: (model: QMUIMediaModel) -> Unit\n) {\n    val pickedIndex = remember(pickedItems) {\n        pickedItems.indexOfFirst {\n            it == data.model.id\n        }\n    }\n    Box(\n        modifier = Modifier\n            .size(cellSize)\n            .border(OnePx(), QMUILocalPickerConfig.current.gridBorderColor)\n            .clickable(\n                interactionSource = remember {\n                    MutableInteractionSource()\n                },\n                indication = null,\n                enabled = true\n            ) {\n                onPreview.invoke(data.model)\n            }\n    ) {\n        val thumbnail = remember(data) {\n            data.photoProvider.thumbnail(true)\n        }\n        thumbnail?.Compose(\n            contentScale = ContentScale.Crop,\n            isContainerDimenExactly = true,\n            onSuccess = null,\n            onError = null\n        )\n\n        QMUIPhotoPickerGridCellMask(pickedIndex)\n\n        Box(\n            modifier = Modifier\n                .align(Alignment.TopEnd)\n                .clickable {\n                    onPickItem(pickedIndex < 0, data)\n                }\n                .padding(4.dp)\n                .size(24.dp),\n            contentAlignment = Alignment.Center\n        ) {\n            QMUIPhotoPickCheckBox(pickedIndex)\n        }\n    }\n}\n\n@Composable\nfun QMUIPhotoPickerGridCellMask(pickedIndex: Int){\n    val maskAlpha = animateFloatAsState(targetValue = if(pickedIndex >= 0) 0.36f else 0.15f)\n    Box(\n        modifier = Modifier\n            .fillMaxSize()\n            .background(Color.Black.copy(alpha = maskAlpha.value))\n    )\n}\n\n@Composable\nfun QMUIPhotoPickerGridToolBar(\n    modifier: Modifier = Modifier,\n    enableOrigin: Boolean,\n    pickedItems: List<Long>,\n    isOriginOpenFlow: StateFlow<Boolean>,\n    onToggleOrigin: (toOpen: Boolean) -> Unit,\n    onPreview: () -> Unit\n) {\n    val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n        WindowInsetsCompat.Type.navigationBars()\n    ).dp()\n    val config = QMUILocalPickerConfig.current\n    Box(modifier = modifier\n        .background(config.toolBarBgColor)\n        .padding(bottom = insets.bottom)\n        .height(44.dp)\n        .drawBehind {\n            drawTopSeparator(config.commonSeparatorColor)\n        }\n    ) {\n        CommonTextButton(\n            modifier = Modifier.align(Alignment.CenterStart),\n            enable = pickedItems.isNotEmpty(),\n            text = \"预览\",\n            onClick = onPreview\n        )\n\n        if(enableOrigin){\n            OriginOpenButton(\n                modifier = Modifier\n                    .fillMaxHeight()\n                    .padding(horizontal = 16.dp)\n                    .align(Alignment.Center),\n                isOriginOpenFlow = isOriginOpenFlow,\n                onToggleOrigin = onToggleOrigin\n            )\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/PaintEdit.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.*\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.IntSize\nimport androidx.compose.ui.unit.dp\n\nsealed class EditPaint {\n    @Composable\n    abstract fun Compose(size: Dp, selected: Boolean, onClick: () -> Unit)\n}\n\nclass MosaicEditPaint(\n    val scaleLevel: Int\n) : EditPaint() {\n\n    @Composable\n    override fun Compose(size: Dp, selected: Boolean, onClick: () -> Unit) {\n        val ringWidth = with(LocalDensity.current) {\n            2.dp.toPx()\n        }\n        androidx.compose.foundation.Canvas(modifier = Modifier\n            .size(size)\n            .clickable(\n                interactionSource = remember {\n                    MutableInteractionSource()\n                },\n                indication = null\n            ) {\n                onClick()\n            }) {\n            drawCircle(\n                Color.White,\n                radius = this.size.minDimension / 2 - if (selected) 0f else ringWidth\n            )\n            drawCircle(\n                Color.Black,\n                radius = this.size.minDimension / 2 - ringWidth * 2\n            )\n        }\n    }\n}\n\nclass ColorEditPaint(val color: Color) : EditPaint() {\n    @Composable\n    override fun Compose(size: Dp, selected: Boolean, onClick: () -> Unit) {\n        val ringWidth = with(LocalDensity.current) {\n            2.dp.toPx()\n        }\n        androidx.compose.foundation.Canvas(modifier = Modifier\n            .size(size)\n            .clickable(\n                interactionSource = remember {\n                    MutableInteractionSource()\n                },\n                indication = null\n            ) {\n                onClick()\n            }) {\n            drawCircle(\n                Color.White,\n                radius = this.size.minDimension / 2 - if (selected) 0f else ringWidth\n            )\n            drawCircle(\n                color,\n                radius = this.size.minDimension / 2 - ringWidth * 2\n            )\n        }\n    }\n}\n\nsealed class PaintEditLayer(val path: Path) {\n    abstract fun DrawScope.draw()\n    abstract fun drawToBitmap()\n}\n\nclass GraffitiEditLayer(\n    path: Path,\n    val color: Color,\n    val strokeWidth: Float\n) : PaintEditLayer(path) {\n\n    override fun DrawScope.draw() {\n        drawPath(\n            path,\n            color = color,\n            style = Stroke(\n                width = strokeWidth,\n                cap = StrokeCap.Round,\n                join = StrokeJoin.Round\n            )\n        )\n    }\n\n    override fun drawToBitmap() {\n\n    }\n}\n\nclass MosaicEditLayer(\n    path: Path,\n    val image: ImageBitmap,\n    val strokeWidth: Float\n) : PaintEditLayer(path) {\n\n\n    private val paint = Paint()\n\n    override fun DrawScope.draw() {\n        if (!path.isEmpty) {\n            drawContext.canvas.withSaveLayer(Rect(Offset.Zero, drawContext.size), paint) {\n                drawPath(\n                    path,\n                    Color.White,\n                    style = Stroke(\n                        width = strokeWidth,\n                        cap = StrokeCap.Round,\n                        join = StrokeJoin.Round\n                    )\n                )\n                drawImage(\n                    image,\n                    dstSize = IntSize(\n                        drawContext.size.width.toInt(),\n                        drawContext.size.height.toInt()\n                    ),\n                    blendMode = BlendMode.SrcIn,\n                    filterQuality = FilterQuality.None\n                )\n            }\n        }\n    }\n\n    override fun drawToBitmap() {\n\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/Preview.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy\nimport androidx.compose.foundation.lazy.LazyRow\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.drawBehind\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.dp\nimport androidx.core.view.WindowInsetsCompat\nimport com.google.accompanist.pager.ExperimentalPagerApi\nimport com.google.accompanist.pager.HorizontalPager\nimport com.google.accompanist.pager.PagerState\nimport com.qmuiteam.compose.core.ex.drawTopSeparator\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\nimport com.qmuiteam.photo.compose.QMUIGesturePhoto\nimport com.qmuiteam.photo.data.PhotoLoadStatus\nimport com.qmuiteam.photo.data.QMUIMediaPhotoVO\nimport kotlinx.coroutines.flow.StateFlow\n\n@OptIn(ExperimentalPagerApi::class)\n@Composable\nfun QMUIPhotoPickerPreview(\n    pagerState: PagerState,\n    data: List<QMUIMediaPhotoVO>,\n    loading: @Composable BoxScope.() -> Unit,\n    loadingFailed: @Composable BoxScope.() -> Unit,\n    onTap: () -> Unit\n) {\n\n    HorizontalPager(\n        count = data.size,\n        state = pagerState\n    ) { page ->\n        val item = data[page]\n        BoxWithConstraints(modifier = Modifier.fillMaxSize()) {\n            QMUIGesturePhoto(\n                containerWidth = maxWidth,\n                containerHeight = maxHeight,\n                imageRatio = item.model.ratio(),\n                shouldTransitionEnter = false,\n                shouldTransitionExit = false,\n                isLongImage = item.photoProvider.isLongImage(),\n                onBeginPullExit = {\n                    false\n                },\n                onTapExit = {\n                    onTap()\n                }\n            ) { _, _, _, onImageRatioEnsured ->\n                QMUIPhotoPickerPreviewItemContent(item, onImageRatioEnsured, loadingFailed, loading)\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerPreviewItemContent(\n    item: QMUIMediaPhotoVO,\n    onImageRatioEnsured: (Float) -> Unit,\n    loading: @Composable BoxScope.() -> Unit,\n    loadingFailed: @Composable BoxScope.() -> Unit,\n) {\n\n    val photo = remember(item) {\n        item.photoProvider.photo()\n    }\n\n    var loadStatus by remember {\n        mutableStateOf(PhotoLoadStatus.loading)\n    }\n\n    Box(modifier = Modifier.fillMaxSize()) {\n\n        photo?.Compose(\n            contentScale = ContentScale.Fit,\n            isContainerDimenExactly = true,\n            onSuccess = {\n                if (it.drawable.intrinsicWidth > 0 && it.drawable.intrinsicHeight > 0) {\n                    onImageRatioEnsured(it.drawable.intrinsicWidth.toFloat() / it.drawable.intrinsicHeight)\n                }\n                loadStatus = PhotoLoadStatus.success\n            },\n            onError = {\n                loadStatus = PhotoLoadStatus.failed\n            }\n        )\n\n        if (loadStatus == PhotoLoadStatus.loading) {\n            loading()\n        } else if (loadStatus == PhotoLoadStatus.failed) {\n            loadingFailed()\n        }\n    }\n}\n\n@Composable\nfun QMUIPhotoPickerPreviewPickedItems(\n    data: List<QMUIMediaPhotoVO>,\n    pickedItems: List<Long>,\n    currentId: Long,\n    onClick: (QMUIMediaPhotoVO) -> Unit\n) {\n    if (pickedItems.isNotEmpty()) {\n        val list = remember(data, pickedItems) {\n            data.filter { pickedItems.contains(it.model.id) }\n        }\n        LazyRow(\n            modifier = Modifier\n                .fillMaxWidth()\n                .height(60.dp)\n                .background(QMUILocalPickerConfig.current.toolBarBgColor),\n            verticalAlignment = Alignment.CenterVertically,\n            horizontalArrangement = spacedBy(5.dp),\n            contentPadding = PaddingValues(horizontal = 5.dp)\n        ) {\n            items(list, { it.model.id }) {\n                QMUIPhotoPickerPreviewPickedItem(it, it.model.id == currentId, onClick)\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun QMUIPhotoPickerPreviewPickedItem(\n    item: QMUIMediaPhotoVO,\n    isCurrent: Boolean,\n    onClick: (QMUIMediaPhotoVO) -> Unit\n) {\n    val thumb = remember(item) {\n        item.photoProvider.thumbnail(true)\n    }\n    Box(modifier = Modifier\n        .size(50.dp)\n        .clickable(\n            interactionSource = remember { MutableInteractionSource() },\n            indication = null\n        ) {\n            onClick(item)\n        }\n        .let {\n            if (isCurrent) {\n                it.border(2.dp, QMUILocalPickerConfig.current.commonIconCheckedTintColor)\n            } else {\n                it\n            }\n        }\n    ) {\n        thumb?.Compose(\n            contentScale = ContentScale.Crop,\n            isContainerDimenExactly = true,\n            onSuccess = null,\n            onError = null\n        )\n    }\n}\n\n\n@Composable\nfun QMUIPhotoPickerPreviewToolBar(\n    modifier: Modifier = Modifier,\n    current: QMUIMediaPhotoVO,\n    isCurrentPicked: Boolean,\n    enableOrigin: Boolean,\n    isOriginOpenFlow: StateFlow<Boolean>,\n    onToggleOrigin: (toOpen: Boolean) -> Unit,\n    onEdit: () -> Unit,\n    onToggleSelect: (toSelect: Boolean) -> Unit\n) {\n    val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n        WindowInsetsCompat.Type.navigationBars()\n    ).dp()\n    val config = QMUILocalPickerConfig.current\n    Box(modifier = modifier\n        .background(config.toolBarBgColor)\n        .padding(bottom = insets.bottom)\n        .height(44.dp)\n        .drawBehind {\n            drawTopSeparator(config.commonSeparatorColor)\n        }\n    ) {\n        if (current.model.editable && config.editable) {\n            CommonTextButton(\n                modifier = Modifier.align(Alignment.CenterStart),\n                enable = true,\n                text = \"编辑\",\n                onClick = onEdit\n            )\n        }\n\n        if (enableOrigin) {\n            OriginOpenButton(\n                modifier = Modifier\n                    .fillMaxHeight()\n                    .padding(horizontal = 16.dp)\n                    .align(Alignment.Center),\n                isOriginOpenFlow = isOriginOpenFlow,\n                onToggleOrigin = onToggleOrigin\n            )\n        }\n\n        PickCurrentCheckButton(\n            modifier = Modifier\n                .fillMaxHeight()\n                .padding(horizontal = 16.dp)\n                .align(Alignment.CenterEnd),\n            isPicked = isCurrentPicked,\n            onPicked = onToggleSelect\n        )\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/TextEdit.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport android.graphics.Typeface\nimport android.text.TextPaint\nimport android.util.Log\nimport androidx.compose.animation.*\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.gestures.*\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.ExperimentalComposeUiApi\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.draw.rotate\nimport androidx.compose.ui.geometry.CornerRadius\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphics.*\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.graphics.drawscope.drawIntoCanvas\nimport androidx.compose.ui.input.pointer.consumeAllChanges\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.input.pointer.positionChangeConsumed\nimport androidx.compose.ui.input.pointer.positionChanged\nimport androidx.compose.ui.layout.onGloballyPositioned\nimport androidx.compose.ui.layout.onPlaced\nimport androidx.compose.ui.layout.positionInWindow\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.unit.IntSize\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.core.view.WindowInsetsCompat\nimport com.qmuiteam.compose.core.R\nimport com.qmuiteam.compose.core.provider.QMUILocalWindowInsets\nimport com.qmuiteam.compose.core.provider.dp\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlin.math.PI\nimport kotlin.math.abs\nimport kotlin.math.absoluteValue\n\ninternal class TextEditLayer(\n    val text: String,\n    val fontSize: TextUnit,\n    val center: Offset,\n    val color: Color,\n    val reverse: Boolean,\n    val offsetFlow: MutableStateFlow<Offset> = MutableStateFlow(Offset.Zero),\n    val scaleFlow: MutableStateFlow<Float> = MutableStateFlow(1f),\n    val rotationFlow: MutableStateFlow<Float> = MutableStateFlow(0f)\n) {\n\n    val isFocusFlow = MutableStateFlow(false)\n\n    private val textColor = if (reverse) {\n        (if (color == Color.White) Color.Black else Color.White)\n    } else color\n\n    private val paint = TextPaint().apply {\n        typeface = Typeface.DEFAULT_BOLD\n        color = textColor.toArgb()\n        setShadowLayer(0f, 2f, 2f, textColor.copy(0.4f).toArgb())\n    }\n\n\n    @Composable\n    private fun TextLayout(\n        modifier: Modifier,\n        lineSpace: Float,\n        paddingHor: Float,\n        paddingVer: Float,\n        fontSize: Float,\n        isFocus: Boolean\n    ) {\n        val cornerRadius = with(LocalDensity.current) {\n            10.dp.toPx()\n        }\n\n        val focusPointSize = with(LocalDensity.current) {\n            6.dp.toPx()\n        }\n        val focusLineWidth = with(LocalDensity.current) {\n            2.dp.toPx()\n        }\n\n        Canvas(modifier = modifier) {\n\n            val rectTopLeftOffset = Offset(focusPointSize / 2, focusPointSize / 2)\n            val rectSize = Size(size.width - focusPointSize, size.height - focusPointSize)\n\n            if (reverse) {\n                drawRoundRect(\n                    color,\n                    topLeft = rectTopLeftOffset,\n                    size = rectSize,\n                    cornerRadius = CornerRadius(cornerRadius, cornerRadius)\n                )\n            }\n            paint.textSize = fontSize\n            drawIntoCanvas {\n                val fontHeight = paint.descent() - paint.ascent()\n                var baseLine = paddingVer - paint.ascent()\n                var start = 0\n                while (start < text.length) {\n                    val count = paint.breakText(\n                        text, start, text.length,\n                        false,\n                        size.width - paddingHor * 2,\n                        null\n                    )\n                    val end = start + count\n                    val contentWidth = paint.measureText(text, start, end)\n                    it.nativeCanvas.drawText(text, start, end, (size.width - contentWidth) / 2, baseLine, paint)\n                    baseLine += fontHeight + lineSpace\n                    start = end\n                }\n            }\n\n            if (isFocus) {\n                drawRect(\n                    Color.White,\n                    topLeft = rectTopLeftOffset,\n                    size = rectSize,\n                    style = Stroke(focusLineWidth)\n                )\n                val focusSize = Size(focusPointSize, focusPointSize)\n                drawRect(\n                    Color.White,\n                    topLeft = Offset.Zero,\n                    size = focusSize\n                )\n                drawRect(\n                    Color.White,\n                    topLeft = Offset(size.width - focusPointSize, 0f),\n                    size = focusSize\n                )\n                drawRect(\n                    Color.White,\n                    topLeft = Offset(0f, size.height - focusPointSize),\n                    size = focusSize\n                )\n                drawRect(\n                    Color.White,\n                    topLeft = Offset(size.width - focusPointSize, size.height - focusPointSize),\n                    size = focusSize\n                )\n            }\n        }\n    }\n\n    @OptIn(ExperimentalComposeUiApi::class)\n    @Composable\n    fun Content(\n        layoutInfo: PickerPhotoLayoutInfo,\n        onFocus: () -> Unit,\n        onEdit: () -> Unit,\n        onToggleDragging: (Boolean) -> Unit,\n        onDelete: () -> Unit\n    ) {\n        val currentOffset by offsetFlow.collectAsState()\n        val currentRotation by rotationFlow.collectAsState()\n        val currentScale by scaleFlow.collectAsState()\n\n        val lineSpace = with(LocalDensity.current) {\n            QMUILocalPickerConfig.current.textEditLineSpace.toPx()\n        }\n\n        val fontSizePx = with(LocalDensity.current) {\n            fontSize.toPx()\n        }\n\n        val paddingHor = with(LocalDensity.current) {\n            16.dp.toPx()\n        }\n\n        val paddingVer = with(LocalDensity.current) {\n            8.dp.toPx()\n        }\n\n        val isFocus by isFocusFlow.collectAsState()\n\n        BoxWithConstraints(modifier = Modifier.fillMaxSize()) {\n            val (contentWidth, contentHeight) = remember(constraints.maxWidth, constraints.maxHeight, fontSizePx) {\n                paint.textSize = fontSizePx\n                val textConstraintMaxWidth = constraints.maxWidth - paddingHor * 4\n                val fontHeight = paint.descent() - paint.ascent()\n                var start = 0\n                var textMaxWidth = 0f\n                var lineCount = 0\n                while (start < text.length) {\n                    val count = paint.breakText(\n                        text, start, text.length,\n                        false,\n                        textConstraintMaxWidth,\n                        null\n                    )\n                    val end = start + count\n                    val contentWidth = paint.measureText(text, start, end)\n                    textMaxWidth = textMaxWidth.coerceAtLeast(contentWidth)\n                    lineCount++\n                    start = end\n                }\n                arrayOf(\n                    textMaxWidth + paddingHor * 2,\n                    lineCount * (fontHeight + lineSpace) - lineSpace + paddingVer * 2\n                )\n            }\n            val contentWidthDp = with(LocalDensity.current) {\n                contentWidth.toDp()\n            }\n            val contentHeightDp = with(LocalDensity.current) {\n                contentHeight.toDp()\n            }\n\n            val start = with(LocalDensity.current) {\n                (center.x - contentWidth / 2).toDp()\n            }\n\n            val top = with(LocalDensity.current) {\n                (center.y - contentHeight / 2).toDp()\n            }\n\n            val dragInfo = remember {\n                MutableDragInfo()\n            }\n\n            var isDragging by remember {\n                mutableStateOf(false)\n            }\n\n            var isInDeleteArea by remember {\n                mutableStateOf(false)\n            }\n\n            TextLayout(\n                modifier = Modifier\n                    .graphicsLayer {\n                        transformOrigin = TransformOrigin(0f, 0f)\n                        scaleX = layoutInfo.scale\n                        scaleY = layoutInfo.scale\n                        translationX = layoutInfo.rect.left\n                        translationY = layoutInfo.rect.top\n                    }\n                    .padding(start = start, top = top)\n                    .width(contentWidthDp)\n                    .height(contentHeightDp)\n                    .onGloballyPositioned {\n                        dragInfo.editLayerCenter = it.positionInWindow() + Offset(it.size.width / 2f, it.size.height / 2f)\n                    }\n                    .graphicsLayer {\n                        translationX = currentOffset.x\n                        translationY = currentOffset.y\n                        scaleX = currentScale\n                        scaleY = currentScale\n                        rotationZ = currentRotation\n                    }\n                    .pointerInput(\"\") {\n                        coroutineScope {\n\n                            launch {\n                                detectTapGestures(\n                                    onTap = {\n                                        if (isFocusFlow.value) {\n                                            onEdit()\n                                        } else {\n                                            isFocusFlow.value = true\n                                            onFocus()\n                                        }\n                                    },\n                                )\n                            }\n                            launch {\n                                forEachGesture {\n                                    awaitPointerEventScope {\n                                        var rotation = 0f\n                                        var zoom = 1f\n                                        var pan = Offset.Zero\n                                        var pastTouchSlop = false\n                                        val touchSlop = viewConfiguration.touchSlop\n\n                                        awaitFirstDown(requireUnconsumed = false)\n                                        do {\n                                            val event = awaitPointerEvent()\n                                            val canceled = event.changes.any { it.positionChangeConsumed() }\n                                            if (!canceled) {\n                                                val zoomChange = event.calculateZoom()\n                                                val rotationChange = event.calculateRotation()\n                                                val panChange = event.calculatePan()\n\n                                                if (!pastTouchSlop) {\n                                                    zoom *= zoomChange\n                                                    rotation += rotationChange\n                                                    pan += panChange\n\n                                                    val centroidSize = event.calculateCentroidSize(useCurrent = false)\n                                                    val zoomMotion = abs(1 - zoom) * centroidSize\n                                                    val rotationMotion = abs(rotation * PI.toFloat() * centroidSize / 180f)\n                                                    val panMotion = pan.getDistance()\n\n                                                    if (zoomMotion > touchSlop ||\n                                                        rotationMotion > touchSlop ||\n                                                        panMotion > touchSlop\n                                                    ) {\n                                                        pastTouchSlop = true\n                                                    }\n                                                }\n\n                                                if (pastTouchSlop) {\n                                                    if (rotationChange != 0f ||\n                                                        zoomChange != 1f ||\n                                                        panChange != Offset.Zero\n                                                    ) {\n                                                        if(panChange != Offset.Zero){\n                                                            if(!isDragging){\n                                                                isDragging = true\n                                                                onToggleDragging(true)\n                                                            }\n                                                        }\n                                                        offsetFlow.value = offsetFlow.value + panChange\n                                                        scaleFlow.value = scaleFlow.value * zoomChange\n                                                        rotationFlow.value = rotationFlow.value + rotationChange\n                                                        if (isDragging) {\n                                                            isInDeleteArea = dragInfo.isInDeleteArea(offsetFlow.value)\n                                                        }\n                                                    }\n                                                    event.changes.forEach {\n                                                        if (it.positionChanged()) {\n                                                            it.consumeAllChanges()\n                                                        }\n                                                    }\n                                                }\n                                            }\n                                        } while (!canceled && event.changes.any { it.pressed })\n                                        if (isDragging) {\n                                            if (isInDeleteArea) {\n                                                onDelete()\n                                            }\n                                        }\n                                        isInDeleteArea = false\n                                        isDragging = false\n                                        onToggleDragging(false)\n                                    }\n                                }\n                            }\n                        }\n                    },\n                lineSpace = lineSpace,\n                paddingHor = paddingHor,\n                paddingVer = paddingVer,\n                fontSize = fontSizePx,\n                isFocus = isFocus\n            )\n\n            AnimatedVisibility(\n                visible = isDragging,\n                modifier = Modifier.align(Alignment.BottomCenter),\n                enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),\n                exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()\n            ) {\n                DeleteArea(isInDeleteArea) { offset, size ->\n                    dragInfo.deleteAreaOffset = offset\n                    dragInfo.deleteAreaSize = size\n                }\n            }\n        }\n    }\n\n    @Composable\n    private fun DeleteArea(\n        isFocusing: Boolean,\n        onPlaced: (offset: Offset, size: IntSize) -> Unit\n    ) {\n        val insets = QMUILocalWindowInsets.current.getInsetsIgnoringVisibility(\n            WindowInsetsCompat.Type.navigationBars()\n        ).dp()\n        val config = QMUILocalPickerConfig.current\n        Column(modifier = Modifier\n            .padding(bottom = insets.bottom + 16.dp)\n            .clip(RoundedCornerShape(8.dp))\n            .onGloballyPositioned {\n                onPlaced(it.positionInWindow(), it.size)\n            }\n            .background(if (isFocusing) config.editLayerDeleteAreaNormalFocusColor else config.editLayerDeleteAreaNormalBgColor)\n            .padding(horizontal = 24.dp, vertical = 16.dp),\n            horizontalAlignment = Alignment.CenterHorizontally\n        ) {\n            Image(\n                painter = painterResource(\n                    id = if (isFocusing) {\n                        R.drawable.ic_qmui_checkbox_checked\n                    } else R.drawable.ic_qmui_checkbox_partial\n                ),\n                contentDescription = \"\",\n                colorFilter = ColorFilter.tint(Color.White)\n            )\n            Spacer(modifier = Modifier.height(10.dp))\n            Text(\n                text = if (isFocusing) \"松手即可删除\" else \"拖动到此处删除\",\n                color = Color.White,\n                fontSize = 15.sp\n            )\n        }\n    }\n}\n\nprivate class MutableDragInfo(\n    var deleteAreaOffset: Offset = Offset.Zero,\n    var deleteAreaSize: IntSize = IntSize.Zero,\n    var editLayerCenter: Offset = Offset.Zero\n) {\n    fun isInDeleteArea(offset: Offset): Boolean {\n        val windowOffset = editLayerCenter + offset\n        return windowOffset.x > deleteAreaOffset.x &&\n                windowOffset.x < deleteAreaOffset.x + deleteAreaSize.width &&\n                windowOffset.y > deleteAreaOffset.y &&\n                windowOffset.y < deleteAreaOffset.y + deleteAreaSize.height\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/compose/picker/TopBarItem.kt",
    "content": "package com.qmuiteam.photo.compose.picker\n\nimport androidx.compose.animation.core.animateFloat\nimport androidx.compose.animation.core.tween\nimport androidx.compose.animation.core.updateTransition\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.interaction.MutableInteractionSource\nimport androidx.compose.foundation.interaction.collectIsPressedAsState\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.clip\nimport androidx.compose.ui.draw.rotate\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Path\nimport androidx.compose.ui.graphics.drawscope.Stroke\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport com.qmuiteam.compose.core.ui.QMUITopBarItem\nimport kotlinx.coroutines.flow.StateFlow\n\nclass QMUIPhotoPickerBucketTopBarItem(\n    private val bgColor: Color,\n    private val textColor: Color,\n    private val iconBgColor: Color,\n    private val iconColor: Color,\n    private val textFlow: StateFlow<String>,\n    private val isFocusFlow: StateFlow<Boolean>,\n    private val onClick: () -> Unit\n) : QMUITopBarItem {\n\n    @Composable\n    override fun Compose(topBarHeight: Dp) {\n        val text by textFlow.collectAsState()\n        Row(\n            modifier = Modifier\n                .clip(CircleShape)\n                .background(bgColor)\n                .clickable(\n                    interactionSource = remember { MutableInteractionSource() },\n                    indication = null,\n                    enabled = true,\n                ) {\n                    onClick()\n                }\n                .padding(start = 12.dp, end = 6.dp, top = 4.dp, bottom = 4.dp),\n            verticalAlignment = Alignment.CenterVertically,\n            horizontalArrangement = Arrangement.Absolute.spacedBy(5.dp)\n        ) {\n            Text(\n                text,\n                fontSize = 17.sp,\n                color = textColor,\n                modifier = Modifier.padding(bottom = 1.dp)\n            )\n            QMUIPhotoPickerBucketToggleArrow(iconBgColor, iconColor, isFocusFlow)\n        }\n    }\n}\n\nclass QMUIPhotoSendTopBarItem(\n    private val canSendSelf: Boolean,\n    private val text: String,\n    private val maxSelectCount: Int,\n    private val selectCountFlow: StateFlow<Int>,\n    private val onClick: () -> Unit\n) : QMUITopBarItem {\n    @Composable\n    override fun Compose(topBarHeight: Dp) {\n        val selectCount by selectCountFlow.collectAsState()\n        CommonButton(\n            enabled = selectCount > 0 || canSendSelf,\n            text = if (selectCount > 0) \"$text($selectCount/$maxSelectCount)\" else text,\n            onClick = onClick\n        )\n    }\n}\n\n@Composable\nfun QMUIPhotoPickerBucketToggleArrow(\n    bgColor: Color,\n    iconColor: Color,\n    isFocusFlow: StateFlow<Boolean>\n) {\n    val isFocus by isFocusFlow.collectAsState()\n    Box(\n        modifier = Modifier\n            .size(20.dp)\n            .clip(CircleShape)\n            .background(bgColor),\n        contentAlignment = Alignment.Center\n    ) {\n        val strokeWidth = with(LocalDensity.current) {\n            1.6.dp.toPx()\n        }\n        val transition = updateTransition(targetState = isFocus, \"QMUIPhotoPickerBucketToggleArrow\")\n        val rotate = transition.animateFloat(\n            transitionSpec = { tween(durationMillis = 300) },\n            label = \"QMUIPhotoPickerBucketToggleArrowFocus\"\n        ) {\n            if (it) 180f else 0f\n        }\n        Canvas(\n            modifier = Modifier\n                .width(8.dp)\n                .height(4.dp)\n                .rotate(rotate.value)\n        ) {\n\n            drawPath(Path().apply {\n                moveTo(0f, 0f)\n                lineTo(size.width / 2, size.height)\n                lineTo(size.width, 0f)\n            }, iconColor, style = Stroke(strokeWidth))\n        }\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/data/QMUIBitmapRegion.kt",
    "content": "package com.qmuiteam.photo.data\n\nimport android.graphics.*\nimport android.graphics.drawable.Drawable\nimport android.os.Build\nimport android.util.LruCache\nimport androidx.compose.ui.unit.IntSize\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport java.io.InputStream\nimport kotlin.math.max\nimport kotlin.math.min\n\n\nfun interface QMUIBitmapRegionLoader {\n    suspend fun load(): Bitmap?\n}\n\nclass QMUIBitmapRegionProvider(\n    val width: Int,\n    val height: Int,\n    val loader: QMUIBitmapRegionLoader\n)\n\nclass QMUIAlreadyBitmapRegionLoader(private val bm: Bitmap) : QMUIBitmapRegionLoader {\n    override suspend fun load(): Bitmap {\n        return bm\n    }\n}\n\nprivate class QMUICacheBitmapRegionLoader(\n    private val origin: QMUIBitmapRegionLoader,\n    private val cacheStatistic: QMUIBitmapRegionCacheStatistic\n) : QMUIBitmapRegionLoader {\n\n    @Volatile\n    private var cache: Bitmap? = null\n    private val mutex = Mutex()\n\n    override suspend fun load(): Bitmap? {\n        val localCache = cache\n        if (localCache != null) {\n            return localCache\n        }\n        return mutex.withLock {\n            if (cache != null) {\n                return cache\n            }\n            origin.load().also {\n                cache = it\n                cacheStatistic.doWhenLoaded(this)\n            }\n        }\n    }\n\n    suspend fun releaseCache() {\n        mutex.withLock {\n            cache = null\n        }\n    }\n}\n\nclass QMUIBitmapRegion(val width: Int, val height: Int, val list: List<QMUIBitmapRegionProvider>)\n\n\n/**\n * fit:\n *  if ture, fit the image to the dst so that both dimensions (width and height) of the image will be equal to or less than the dst\n *  if false, fill the image in the dst such that both dimensions (width and height) of the image will be equal to or larger than the dst\n */\nfun loadLongImageThumbnail(\n    ins: InputStream,\n    preferredSize: IntSize,\n    options: BitmapFactory.Options,\n    fit: Boolean = false,\n): Bitmap? {\n    return loadLongImage(ins, preferredSize, options, fit) { regionDecoder ->\n        val w = regionDecoder.width\n        val h = regionDecoder.height\n        val pageHeight = if (preferredSize.width > 0 && preferredSize.height > 0) {\n            (w * preferredSize.height / preferredSize.width).coerceAtMost(w * 5).coerceAtMost(h)\n        } else {\n            (5 * w).coerceAtMost(h)\n        }\n        regionDecoder.decodeRegion(Rect(0, 0, w, pageHeight), options)\n    }\n}\n\n/**\n * fit:\n *  if ture, fit the image to the dst so that both dimensions (width and height) of the image will be equal to or less than the dst\n *  if false, fill the image in the dst such that both dimensions (width and height) of the image will be equal to or larger than the dst\n */\nfun loadLongImage(\n    ins: InputStream,\n    preferredSize: IntSize,\n    options: BitmapFactory.Options,\n    fit: Boolean = false,\n    preloadCount: Int = Int.MAX_VALUE,\n    cacheTimeoutForLazyLoad: Long = 1000,\n    cacheCountForLazyLoad: Int = 5\n): QMUIBitmapRegion {\n    val cacheStatistic = QMUIBitmapRegionCacheStatistic(cacheTimeoutForLazyLoad, cacheCountForLazyLoad)\n    return loadLongImage(ins, preferredSize, options, fit) { regionDecoder ->\n        val w = regionDecoder.width\n        val h = regionDecoder.height\n        val pageHeight = if (preferredSize.width > 0 && preferredSize.height > 0) {\n            (w * preferredSize.height / preferredSize.width).coerceAtMost(w * 5).coerceAtMost(h)\n        } else {\n            (5 * w).coerceAtMost(h)\n        }\n\n        val ret = arrayListOf<QMUIBitmapRegionProvider>()\n        var top = 0\n        var i = 0\n        while (top < h) {\n            val bottom = (top + pageHeight).coerceAtMost(h)\n            if (i < preloadCount) {\n                val bm = regionDecoder.decodeRegion(Rect(0, top, w, bottom), options)\n                ret.add(QMUIBitmapRegionProvider(bm.width, bm.height, QMUIAlreadyBitmapRegionLoader(bm)))\n            } else {\n                val finalTop = top\n                val loader = object : QMUIBitmapRegionLoader {\n\n                    private val mutex = Mutex()\n\n                    override suspend fun load(): Bitmap? {\n                        return mutex.withLock {\n                            regionDecoder.decodeRegion(Rect(0, finalTop, w, bottom), options)\n                        }\n                    }\n\n                }\n                ret.add(\n                    QMUIBitmapRegionProvider(\n                        w, bottom - finalTop, if (cacheStatistic.canCache()) {\n                            QMUICacheBitmapRegionLoader(loader, cacheStatistic)\n                        } else {\n                            loader\n                        }\n                    )\n                )\n            }\n            top = bottom\n            i++\n        }\n\n        QMUIBitmapRegion(w, h, ret)\n    }\n}\n\n\nprivate fun <T> loadLongImage(\n    ins: InputStream,\n    preferredSize: IntSize,\n    options: BitmapFactory.Options,\n    fit: Boolean = false,\n    handler: (BitmapRegionDecoder) -> T\n): T {\n    // Read the image's dimensions.\n    options.inJustDecodeBounds = true\n    val bufferedIns = ins.buffered()\n    bufferedIns.mark(Int.MAX_VALUE)\n    BitmapFactory.decodeStream(bufferedIns, null, options)\n    options.inJustDecodeBounds = false\n    bufferedIns.reset()\n\n    options.inMutable = false\n\n    if (options.outWidth > 0 && options.outHeight > 0) {\n        val dstWidth = if (preferredSize.width <= 0) options.outWidth else preferredSize.width\n        val dstHeight = if (preferredSize.height <= 0) options.outHeight else preferredSize.height\n        options.inSampleSize = calculateInSampleSize(\n            srcWidth = options.outWidth,\n            srcHeight = options.outHeight,\n            dstWidth = dstWidth,\n            dstHeight = dstHeight,\n            fit = fit\n        )\n    } else {\n        options.inSampleSize = 1\n    }\n\n    val regionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n        BitmapRegionDecoder.newInstance(bufferedIns)\n    } else {\n        BitmapRegionDecoder.newInstance(bufferedIns, false)\n    }\n    checkNotNull(regionDecoder) { \"BitmapRegionDecoder newInstance failed.\" }\n    return handler(regionDecoder)\n}\n\nprivate fun calculateInSampleSize(\n    srcWidth: Int,\n    srcHeight: Int,\n    dstWidth: Int,\n    dstHeight: Int,\n    fit: Boolean = false\n): Int {\n    val widthInSampleSize = Integer.highestOneBit(srcWidth / dstWidth)\n    val heightInSampleSize = Integer.highestOneBit(srcHeight / dstHeight)\n    return if (fit) {\n        max(widthInSampleSize, heightInSampleSize).coerceAtLeast(1)\n    } else {\n        min(widthInSampleSize, heightInSampleSize).coerceAtLeast(1)\n    }\n}\n\nprivate class QMUIBitmapRegionCacheStatistic(\n    val cacheTimeoutForLazyLoad: Long,\n    val cacheCountForLazyLoad: Int\n) {\n    private val scope = CoroutineScope(Dispatchers.IO)\n\n    private val cacheJobs = object : LruCache<QMUICacheBitmapRegionLoader, Job>(cacheCountForLazyLoad) {\n        override fun entryRemoved(evicted: Boolean, key: QMUICacheBitmapRegionLoader?, oldValue: Job?, newValue: Job?) {\n            super.entryRemoved(evicted, key, oldValue, newValue)\n            if (newValue == null) {\n                key?.let {\n                    scope.launch {\n                        it.releaseCache()\n                    }\n                }\n            } else {\n                oldValue?.cancel()\n            }\n        }\n    }\n\n    fun doWhenLoaded(loader: QMUICacheBitmapRegionLoader) {\n        val job = scope.launch {\n            delay(cacheTimeoutForLazyLoad)\n            cacheJobs.remove(loader)\n        }\n        cacheJobs.put(loader, job)\n    }\n\n    fun canCache(): Boolean {\n        return cacheTimeoutForLazyLoad > 0 && cacheCountForLazyLoad > 0\n    }\n}\n\n\nclass QMUIBitmapRegionHolderDrawable(val bitmapRegion: QMUIBitmapRegion) : Drawable() {\n\n    override fun getIntrinsicHeight(): Int {\n        return bitmapRegion.height\n    }\n\n    override fun getIntrinsicWidth(): Int {\n        return bitmapRegion.width\n    }\n\n    override fun draw(canvas: Canvas) {\n\n    }\n\n    override fun setAlpha(alpha: Int) {\n\n    }\n\n    override fun setColorFilter(colorFilter: ColorFilter?) {\n\n    }\n\n    override fun getOpacity(): Int {\n        return PixelFormat.OPAQUE\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/data/QMUIMediaDataProvider.kt",
    "content": "package com.qmuiteam.photo.data\n\nimport android.content.ContentUris\nimport android.content.Context\nimport android.database.Cursor\nimport android.net.Uri\nimport android.provider.MediaStore\nimport androidx.core.database.getIntOrNull\nimport androidx.core.database.getLongOrNull\nimport androidx.core.database.getStringOrNull\nimport com.qmuiteam.compose.core.helper.QMUILog\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.File\n\nconst val QMUIMediaPhotoBucketAllId = \"__all__\"\nconst val QMUIMediaPhotoBucketAllName = \"最近项目\"\n\nopen class QMUIMediaModel(\n    val id: Long,\n    val uri: Uri,\n    var width: Int,\n    var height: Int,\n    val rotation: Int,\n    val name: String,\n    val modifyTimeSec: Long,\n    val bucketId: String,\n    val bucketName: String,\n    val editable: Boolean\n) {\n    fun ratio(): Float {\n        if(height <= 0 || width <= 0){\n            return -1f\n        }\n        if(rotation == 90 || rotation == 270){\n            return height.toFloat() / width\n        }\n        return width.toFloat() / height\n    }\n}\n\nclass QMUIMediaPhotoBucket(\n    val id: String,\n    val name: String,\n    val list: List<QMUIMediaModel>\n)\n\nclass QMUIMediaPhotoBucketVO(\n    val id: String,\n    val name: String,\n    val list: List<QMUIMediaPhotoVO>\n)\n\nclass QMUIMediaPhotoVO(\n    val model: QMUIMediaModel,\n    val photoProvider: QMUIPhotoProvider\n)\n\ninterface QMUIMediaPhotoProviderFactory {\n    fun factory(model: QMUIMediaModel): QMUIPhotoProvider\n}\n\ninterface QMUIMediaDataProvider {\n    suspend fun provide(context: Context, supportedMimeTypes: Array<String>): List<QMUIMediaPhotoBucket>\n}\n\nclass QMUIMediaImagesProvider : QMUIMediaDataProvider {\n\n    companion object {\n\n        private const val TAG = \"QMUIMediaDataProvider\"\n\n        val DEFAULT_SUPPORT_MIMETYPES = arrayOf(\n            \"image/jpeg\",\n            \"image/png\",\n            \"image/gif\",\n            \"image/webp\",\n            \"image/heic\",\n            \"image/heif\"\n        )\n\n        private val COLUMNS = arrayOf(\n            MediaStore.Images.Media._ID,\n            MediaStore.Images.Media.DATA,\n            MediaStore.Images.Media.MIME_TYPE,\n            MediaStore.Images.Media.WIDTH,\n            MediaStore.Images.Media.HEIGHT,\n            MediaStore.Images.Media.ORIENTATION,\n            MediaStore.Images.Media.DISPLAY_NAME,\n            MediaStore.Images.Media.DATE_MODIFIED,\n            MediaStore.Images.Media.BUCKET_ID,\n            MediaStore.Images.Media.BUCKET_DISPLAY_NAME\n        )\n    }\n\n    override suspend fun provide(context: Context, supportedMimeTypes: Array<String>): List<QMUIMediaPhotoBucket> {\n        return withContext(Dispatchers.IO) {\n            val selection = if (supportedMimeTypes.isEmpty()) {\n                null\n            } else {\n                val sb = StringBuilder()\n                sb.append(MediaStore.Images.Media.MIME_TYPE)\n                sb.append(\" IN (\")\n                supportedMimeTypes.forEachIndexed { index, s ->\n                    if (index != 0) {\n                        sb.append(\",\")\n                    }\n                    sb.append(\"'\")\n                    sb.append(s)\n                    sb.append(\"'\")\n\n                }\n                sb.append(\")\")\n                sb.toString()\n            }\n            val list = mutableListOf<QMUIMediaModel>()\n            context.applicationContext.contentResolver.query(\n                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,\n                COLUMNS,\n                selection,\n                null,\n                \"${MediaStore.Images.Media.DATE_MODIFIED} DESC\"\n            )?.use { cursor ->\n                if (cursor.moveToFirst()) {\n                    do {\n                        try {\n                            val path = cursor.readString(MediaStore.Images.Media.DATA)\n                            val id = cursor.readLong(MediaStore.Images.Media._ID)\n                            val w = cursor.readInt(MediaStore.Images.Media.WIDTH)\n                            val h = cursor.readInt(MediaStore.Images.Media.HEIGHT)\n                            val o = cursor.readInt(MediaStore.Images.Media.ORIENTATION)\n                            val isRotated = o == 90 || o == 270\n                            list.add(\n                                QMUIMediaModel(\n                                    id,\n                                    ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id),\n                                    if(isRotated) h else w,\n                                    if(isRotated) w else h,\n                                    cursor.readInt(MediaStore.Images.Media.ORIENTATION),\n                                    cursor.readString(MediaStore.Images.Media.DISPLAY_NAME),\n                                    cursor.readLong(MediaStore.Images.Media.DATE_MODIFIED),\n                                    cursor.readString(MediaStore.Images.Media.BUCKET_ID),\n                                    (cursor.readString(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)).let {\n                                        it.ifEmpty { File(path).parent ?: \"\" }\n                                    },\n                                    true\n                                )\n                            )\n                        } catch (e: Exception) {\n                            QMUILog.e(TAG, \"read image data from cursor failed.\", e)\n                        }\n                    } while (cursor.moveToNext())\n                }\n            }\n            val buckets = mutableListOf<MutableMediaPhotoBucket>()\n            val defaultPhotoBucket = MutableMediaPhotoBucket(QMUIMediaPhotoBucketAllId, QMUIMediaPhotoBucketAllName)\n            buckets.add(defaultPhotoBucket)\n            list.forEach { model ->\n                defaultPhotoBucket.list.add(model)\n                if(model.name.isNotBlank()){\n                    val bucket = buckets.find { it.id == model.bucketId} ?:MutableMediaPhotoBucket(model.bucketId, model.bucketName).also {\n                        buckets.add(it)\n                    }\n                    bucket.list.add(model)\n                }\n            }\n\n            buckets.map {\n                QMUIMediaPhotoBucket(it.id, it.name, it.list)\n            }\n        }\n    }\n\n    private class MutableMediaPhotoBucket(\n        val id: String,\n        val name: String\n    ){\n        val list: MutableList<QMUIMediaModel> = mutableListOf()\n    }\n\n}\n\n\nprivate fun <T> Cursor.getColumnIndexAndDoAction(columnName: String, block: (Int) -> T): T? {\n    return try {\n        getColumnIndexOrThrow(columnName).let {\n            if (it < 0) null else block(it)\n        }\n    } catch (e: Throwable) {\n        QMUILog.e(\"QMUIMediaDataProvider\", \"getColumnIndex for $columnName failed.\", e)\n        null\n    }\n}\n\nfun Cursor.readLong(columnName: String): Long = getColumnIndexAndDoAction(columnName) { getLongOrNull(it) } ?: 0\nfun Cursor.readString(columnName: String): String = getColumnIndexAndDoAction(columnName) { getStringOrNull(it) } ?: \"\"\nfun Cursor.readInt(columnName: String): Int = getColumnIndexAndDoAction(columnName) { getIntOrNull(it) } ?: 0\n"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/data/QMUIPhotoTransitionDelivery.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.photo.data\n\nimport androidx.annotation.MainThread\n\ninternal object QMUIPhotoTransitionDelivery {\n    private val dataMap = mutableMapOf<Long, PhotoViewerData>()\n\n    @MainThread\n    fun put(data: PhotoViewerData): Long {\n        val id = System.currentTimeMillis()\n        dataMap[id] = data\n        // memory leak protection\n        val iterator = dataMap.iterator()\n        while (iterator.hasNext()) {\n            val next = iterator.next()\n            if (next.key < id - 20 * 1000) {\n                iterator.remove()\n            }\n        }\n        return id\n    }\n\n    @MainThread\n    fun peek(id: Long): PhotoViewerData? {\n        return dataMap[id]\n    }\n\n    @MainThread\n    fun getAndRemove(id: Long): PhotoViewerData? {\n        return dataMap.remove(id)\n    }\n\n    @MainThread\n    fun remove(id: Long) {\n        dataMap.remove(id)\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/data/QMUIPhotoTransitionInfo.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.photo.data\n\nimport android.graphics.Bitmap\nimport android.graphics.drawable.Drawable\nimport android.net.Uri\nimport android.os.Bundle\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.unit.IntSize\nimport androidx.compose.ui.unit.toSize\nimport com.qmuiteam.photo.compose.QMUILocalPhotoConfig\n\nclass PhotoViewerData(\n    val list: List<QMUIPhotoTransitionInfo>,\n    val index: Int,\n    val background: Bitmap?\n)\n\ninternal enum class PhotoLoadStatus {\n    loading, success, failed\n}\n\nclass PhotoResult(val model: Any, val drawable: Drawable)\n\ninterface QMUIPhoto {\n\n    @Composable\n    fun Compose(\n        contentScale: ContentScale,\n        isContainerDimenExactly: Boolean,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    )\n}\n\ninterface QMUIPhotoProvider {\n    fun thumbnail(openBlankColor: Boolean): QMUIPhoto?\n    fun photo(): QMUIPhoto?\n    fun ratio(): Float = -1f\n    fun isLongImage(): Boolean = false\n\n    fun meta(): Bundle?\n    fun recoverCls(): Class<out PhotoTransitionProviderRecover>?\n}\n\nclass QMUIPhotoTransitionInfo(\n    val photoProvider: QMUIPhotoProvider,\n    var offsetInWindow: Offset?,\n    var size: IntSize?,\n    var photo: Drawable?\n) {\n    fun photoRect(): Rect? {\n        val offset = offsetInWindow\n        val size = size?.toSize()\n        if (offset == null || size == null || size.width == 0f || size.height == 0f) {\n            return null\n        }\n        return Rect(offset, size)\n    }\n\n    fun ratio(): Float {\n        var ratio = photoProvider.ratio()\n        if (ratio <= 0f) {\n            photo?.let {\n                if (it.intrinsicWidth > 0 && it.intrinsicHeight > 0) {\n                    ratio = it.intrinsicWidth.toFloat() / it.intrinsicHeight\n                }\n            }\n        }\n        return ratio\n    }\n}\n\nval lossPhotoProvider = object : QMUIPhotoProvider {\n    override fun thumbnail(openBlankColor: Boolean): QMUIPhoto? {\n        return null\n    }\n\n    override fun photo(): QMUIPhoto? {\n        return null\n    }\n\n    override fun meta(): Bundle? {\n        return null\n    }\n\n    override fun recoverCls(): Class<out PhotoTransitionProviderRecover>? {\n        return null\n    }\n}\n\nval lossPhotoTransitionInfo = QMUIPhotoTransitionInfo(lossPhotoProvider, null, null, null)\n\n\ninterface PhotoTransitionProviderRecover {\n    fun recover(bundle: Bundle): QMUIPhotoTransitionInfo?\n}\n\n\nclass ImageItem(\n    val url: String,\n    val thumbnailUrl: String?,\n    val thumbnail: Bitmap?\n)"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/util/BitmapEx.kt",
    "content": "package com.qmuiteam.photo.util\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport android.util.Log\nimport androidx.core.net.toUri\nimport com.qmuiteam.compose.core.helper.QMUILog\nimport java.io.*\n\n\nval DefaultBitmapCompressMaxSizeStrategy: (Bitmap) -> Int = {\n    val ratio = it.width.toFloat() / it.height\n    if (ratio < 0.33 || ratio > 3) {\n        1024 * 1024 * 8\n    } else {\n        1024 * 1024 * 2\n    }\n}\n\nval DefaultBitmapCompressCanUseMemoryStorage: (Bitmap) -> Boolean = {\n    it.width * it.height < 1080 * 1920\n}\n\nabstract class BitmapCompressResult internal constructor(\n    val compressFormat: Bitmap.CompressFormat,\n    val compressQuality: Int,\n    val width: Int,\n    val height: Int,\n) {\n    abstract fun inputStream(): InputStream?\n}\n\ninternal class BitmapCompressStreamResult(\n    compressFormat: Bitmap.CompressFormat,\n    compressQuality: Int,\n    width: Int,\n    height: Int,\n    private val stream: BitmapCompressStream\n): BitmapCompressResult(compressFormat, compressQuality, width, height){\n\n    override fun inputStream(): InputStream? {\n        return stream.inputStream()\n    }\n}\n\nfun Bitmap.saveToLocal(\n    dir: File,\n    compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,\n    compressQuality: Int = 80,\n): Uri{\n    val suffix = when(compressFormat){\n        Bitmap.CompressFormat.JPEG -> \"jpeg\"\n        Bitmap.CompressFormat.PNG -> \"png\"\n        else -> \"webp\"\n    }\n    val fileName = \"qmui_photo_${System.nanoTime()}.${suffix}\"\n    dir.mkdirs()\n    val destFile = File(dir, fileName)\n    destFile.outputStream().buffered().use {\n        compress(compressFormat, compressQuality, it)\n    }\n    return destFile.toUri()\n}\n\nfun Bitmap.compressByShortEdgeWidthAndByteSize(\n    context: Context,\n    shortEdgeMaxWidth: Int = 1200,\n    byteMaxSizeStrategy: (Bitmap) -> Int = DefaultBitmapCompressMaxSizeStrategy,\n    canUseMemoryStorage: (Bitmap) -> Boolean = DefaultBitmapCompressCanUseMemoryStorage,\n    compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,\n    compressQuality: Int = 80,\n): BitmapCompressResult? {\n\n    var bitmap = this\n    try {\n        val ratio = width.toFloat() / height\n        if (width <= height) {\n            if (width > shortEdgeMaxWidth) {\n                bitmap = Bitmap.createScaledBitmap(this, shortEdgeMaxWidth, (shortEdgeMaxWidth / ratio).toInt(), false)\n            }\n        } else {\n            if (height > shortEdgeMaxWidth) {\n                bitmap = Bitmap.createScaledBitmap(this, (shortEdgeMaxWidth * ratio).toInt(), shortEdgeMaxWidth, false)\n            }\n        }\n    } catch (ignored: OutOfMemoryError) {\n        QMUILog.w(\n            \"compressByShortEdgeWidthAndByteSize\",\n            \"createScaledBitmap failed: shortEdgeMaxWidth = $shortEdgeMaxWidth, width = $width; height = $height\"\n        )\n    }\n\n    val byteMaxSize = byteMaxSizeStrategy(this)\n    val useMemoryStorage = canUseMemoryStorage(this)\n\n    val stream: BitmapCompressStream = if (useMemoryStorage) BitmapCompressMemoryStream() else BitmapCompressFileStream(context.cacheDir)\n    var currentQuality = compressQuality\n    var nextQuality = currentQuality\n    var failCount = 0\n    var succes: Boolean\n    do {\n        stream.reset()\n        currentQuality = nextQuality\n        succes = try {\n            stream.outputStream().use {\n                bitmap.compress(compressFormat, currentQuality, it)\n            }\n        } catch (e: Throwable) {\n            QMUILog.w(\n                \"compressByShortEdgeWidthAndByteSize\",\n                \"compress bitmap failed(compressFormat = $compressFormat; quality = $nextQuality, failCount = $failCount).\", e\n            )\n            false\n        }\n        if (succes) {\n            nextQuality -= 10\n            failCount = 0\n        } else {\n            nextQuality -= 5\n            failCount++\n        }\n    } while ((!succes && failCount < 2 && nextQuality >= 20) || (succes && nextQuality >= 20 && stream.size() > byteMaxSize))\n    if (!succes) {\n        return null\n    }\n    return BitmapCompressStreamResult(compressFormat, currentQuality, bitmap.width, bitmap.height, stream)\n}\n\ninternal interface BitmapCompressStream {\n\n    fun reset()\n\n    fun size(): Int\n\n    fun outputStream(): OutputStream\n\n    fun inputStream(): InputStream?\n}\n\ninternal class BitmapCompressMemoryStream : BitmapCompressStream {\n\n    private val output = ByteArrayOutputStream()\n\n    override fun reset() {\n        output.reset()\n    }\n\n    override fun size(): Int {\n        return output.size()\n    }\n\n    override fun outputStream(): OutputStream {\n        return output\n    }\n\n    override fun inputStream(): InputStream {\n        return ByteArrayInputStream(output.toByteArray())\n    }\n\n}\n\ninternal class BitmapCompressFileStream(val cacheDir: File) : BitmapCompressStream {\n\n    private var file: File? = null\n\n    override fun reset() {\n        file?.delete()\n        file = File(cacheDir, \"qmui-bm-${System.nanoTime()}\")\n    }\n\n    override fun size(): Int {\n        return file?.length()?.toInt() ?: 0\n    }\n\n    override fun outputStream(): OutputStream {\n        return file!!.outputStream().buffered()\n    }\n\n    override fun inputStream(): InputStream? {\n        return file?.inputStream()?.buffered()\n    }\n}\n"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/util/QMUIPhotoHelper.kt",
    "content": "package com.qmuiteam.photo.util\n\nimport android.content.ContentValues\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Environment\nimport android.provider.MediaStore\nimport android.util.Log\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\n\n\nobject QMUIPhotoHelper {\n\n    private const val TAG = \"QMUIPhotoHelper\"\n\n    fun saveToStore(\n        context: Context,\n        bitmap: Bitmap,\n        nameWithoutSuffix: String,\n        dirName: String = Environment.DIRECTORY_PICTURES,\n        compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,\n        compressQuality: Int = 100\n    ): Uri? {\n        val suffix = when (compressFormat) {\n            Bitmap.CompressFormat.JPEG -> \".jpeg\"\n            Bitmap.CompressFormat.PNG -> \".png\"\n            else -> \".webp\"\n        }\n        val mime = when (compressFormat) {\n            Bitmap.CompressFormat.JPEG -> \"image/jpeg\"\n            Bitmap.CompressFormat.PNG -> \"image/png\"\n            else -> \"image/webp\"\n        }\n        return saveToStore(context, \"$nameWithoutSuffix$suffix\", mime, dirName) {\n            bitmap.compress(compressFormat, compressQuality, it)\n        }\n    }\n\n    fun saveToStore(\n        context: Context,\n        name: String,\n        mimeType: String,\n        dirName: String = Environment.DIRECTORY_PICTURES,\n        writer: (OutputStream) -> Unit\n    ): Uri? {\n        val contentValues = ContentValues().apply {\n            put(MediaStore.MediaColumns.DISPLAY_NAME, name)\n            put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis())\n            put(MediaStore.MediaColumns.MIME_TYPE, mimeType)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                put(MediaStore.MediaColumns.RELATIVE_PATH, dirName)\n                put(MediaStore.MediaColumns.IS_PENDING, 1)\n            }\n        }\n\n        val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI\n        var stream: OutputStream? = null\n        var uri: Uri? = null\n        try {\n            uri = context.contentResolver.insert(contentUri, contentValues)\n            if (uri == null) {\n                throw IOException(\"Failed to create new MediaStore record.\")\n            }\n            stream = context.contentResolver.openOutputStream(uri)\n            if (stream == null) {\n                throw IOException(\"Failed to get output stream.\")\n            }\n            writer.invoke(stream)\n            contentValues.clear()\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)\n                context.contentResolver.update(uri, contentValues, null, null)\n            }\n            return uri\n        } catch (e: Throwable) {\n            Log.i(TAG, \"saveToStore failed.\", e)\n            if (uri != null) {\n                context.contentResolver.delete(uri, null, null)\n            }\n        } finally {\n            stream?.close()\n        }\n        return null\n    }\n\n    fun compressByShortEdgeWidthAndByteSize(\n        context: Context,\n        originProvider: (Context) -> InputStream?,\n        shortEdgeMaxWidth: Int = 1200,\n        byteMaxSizeStrategy: (Bitmap) -> Int = DefaultBitmapCompressMaxSizeStrategy,\n        canUseMemoryStorage: (Bitmap) -> Boolean = DefaultBitmapCompressCanUseMemoryStorage,\n        compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,\n        compressQuality: Int = 80\n    ): BitmapCompressResult? {\n        val applicationContext = context.applicationContext\n        val options = BitmapFactory.Options()\n        options.inJustDecodeBounds = true\n        var inputStream = originProvider(applicationContext) ?: return null\n        inputStream.use {\n            BitmapFactory.decodeStream(it, null, options)\n        }\n\n        val imageHeight = options.outHeight\n        val imageWidth = options.outWidth\n        if (imageWidth <= imageHeight) {\n            if (imageWidth > shortEdgeMaxWidth) {\n                options.inSampleSize = Integer.highestOneBit(imageWidth / shortEdgeMaxWidth)\n            }\n        } else {\n            if (imageHeight > shortEdgeMaxWidth) {\n                options.inSampleSize = Integer.highestOneBit(imageHeight / shortEdgeMaxWidth)\n            }\n        }\n        options.inJustDecodeBounds = false\n        inputStream = originProvider(applicationContext) ?: return null\n        val bitmap = inputStream.use {\n            BitmapFactory.decodeStream(it, null, options)\n        } ?: return object : BitmapCompressResult(compressFormat, -1, -1, -1) {\n            override fun inputStream(): InputStream? {\n                return originProvider(applicationContext)\n            }\n\n        }\n        return bitmap.compressByShortEdgeWidthAndByteSize(\n            context,\n            shortEdgeMaxWidth,\n            byteMaxSizeStrategy,\n            canUseMemoryStorage,\n            compressFormat,\n            compressQuality\n        )\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/util/ViewEx.kt",
    "content": "package com.qmuiteam.photo.util\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.drawable.BitmapDrawable\nimport android.os.Build\nimport android.util.Size\nimport android.view.View\nimport android.view.WindowManager\nimport android.widget.ImageView\n\nfun View.asBitmap(): Bitmap? {\n    if (this is ImageView) {\n        val drawable = drawable\n        if (drawable != null && drawable is BitmapDrawable) {\n            return drawable.bitmap\n        }\n    }\n    return try {\n        clearFocus()\n        val bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)\n        val canvas = Canvas()\n        canvas.setBitmap(bm)\n        canvas.save()\n        draw(canvas)\n        canvas.restore()\n        canvas.setBitmap(null)\n        bm\n    } catch (e: Throwable) {\n        e.printStackTrace()\n        null\n    }\n}\n\nfun getWindowSize(context: Context): Size {\n    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager\n        val windowMetrics = wm.currentWindowMetrics\n        Size(windowMetrics.bounds.width(), windowMetrics.bounds.height())\n    } else {\n        val displayMetrics = context.resources.displayMetrics\n        Size(displayMetrics.widthPixels, displayMetrics.heightPixels)\n    }\n}"
  },
  {
    "path": "photo/src/main/java/com/qmuiteam/photo/vm/QMUIPhotoPickerViewModel.kt",
    "content": "package com.qmuiteam.photo.vm\n\nimport android.app.Application\nimport android.net.Uri\nimport androidx.annotation.Keep\nimport androidx.compose.foundation.lazy.LazyListState\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.qmuiteam.compose.core.helper.LogTag\nimport com.qmuiteam.compose.core.helper.QMUILog\nimport com.qmuiteam.photo.activity.*\nimport com.qmuiteam.photo.data.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.util.ArrayList\n\nclass QMUIPhotoPickerViewModel @Keep constructor(\n    val application: Application,\n    val state: SavedStateHandle,\n    val dataProvider: QMUIMediaDataProvider,\n    val supportedMimeTypes: Array<String>\n) : ViewModel(), LogTag {\n\n    val pickLimitCount = state.get<Int>(QMUI_PHOTO_PICK_LIMIT_COUNT) ?: QMUI_PHOTO_DEFAULT_PICK_LIMIT_COUNT\n\n    val enableOrigin = state.get<Boolean>(QMUI_PHOTO_ENABLE_ORIGIN) ?: true\n\n    private val photoProviderFactory: QMUIMediaPhotoProviderFactory\n\n    private val _photoPickerSceneFlow = MutableStateFlow<QMUIPhotoPickerScene>(QMUIPhotoPickerGridScene)\n    val photoPickerSceneFlow = _photoPickerSceneFlow.asStateFlow()\n\n    val gridSceneScrollState = LazyListState()\n\n    var prevScene: QMUIPhotoPickerScene? = null\n        private set\n\n    private val _photoPickerDataFlow = MutableStateFlow(QMUIPhotoPickerData(QMUIPhotoPickerLoadState.permissionChecking, null))\n    val photoPickerDataFlow = _photoPickerDataFlow.asStateFlow()\n\n    private val _pickedMap = mutableMapOf<Long, QMUIMediaPhotoVO>()\n    private val _pickedListFlow = MutableStateFlow<List<Long>>(emptyList())\n    val pickedListFlow = _pickedListFlow.asStateFlow()\n\n    private val _pickedCountFlow = MutableStateFlow(0)\n    val pickedCountFlow = _pickedCountFlow.asStateFlow()\n\n    private val _isOriginOpenFlow = MutableStateFlow(false)\n    val isOriginOpenFlow = _isOriginOpenFlow.asStateFlow()\n\n    init {\n        val photoProviderFactoryClsName =\n            state.get<String>(QMUI_PHOTO_PROVIDER_FACTORY) ?: throw RuntimeException(\"no QMUIMediaPhotoProviderFactory is provided.\")\n        photoProviderFactory = Class.forName(photoProviderFactoryClsName).newInstance() as QMUIMediaPhotoProviderFactory\n    }\n\n    fun updateScene(scene: QMUIPhotoPickerScene) {\n        prevScene = _photoPickerSceneFlow.value\n        _photoPickerSceneFlow.value = scene\n    }\n\n    fun permissionDenied() {\n        _photoPickerDataFlow.value = QMUIPhotoPickerData(QMUIPhotoPickerLoadState.permissionDenied, null)\n    }\n\n    fun permissionGranted() {\n        _photoPickerDataFlow.value = QMUIPhotoPickerData(QMUIPhotoPickerLoadState.dataLoading, null)\n        viewModelScope.launch {\n            try {\n                val data = withContext(Dispatchers.IO) {\n                    dataProvider.provide(application, supportedMimeTypes).map { bucket ->\n                        QMUIMediaPhotoBucketVO(bucket.id, bucket.name, bucket.list.map {\n                            QMUIMediaPhotoVO(it, photoProviderFactory.factory(it))\n                        })\n                    }\n                }\n                val pickedItems = state.get<ArrayList<Uri>>(QMUI_PHOTO_PICKED_ITEMS)\n                if(pickedItems != null){\n                    state.set(QMUI_PHOTO_PICKED_ITEMS, null)\n                    val map = mutableMapOf<Uri, Long>()\n                    _pickedMap.clear()\n                    data.find { it.id == QMUIMediaPhotoBucketAllId}?.list?.let {  list ->\n                        for(element in list){\n                            if(pickedItems.find { it == element.model.uri } != null) {\n                                _pickedMap[element.model.id] = element\n                                map[element.model.uri] = element.model.id\n                            }\n                            if(map.size == pickedItems.size){\n                                break\n                            }\n                        }\n\n                    }\n                    // keep the order.\n                    val list = pickedItems.mapNotNull {\n                        map[it]\n                    }\n                    _pickedListFlow.value = list\n                    _pickedCountFlow.value = list.size\n                }\n\n                _photoPickerDataFlow.value = QMUIPhotoPickerData(QMUIPhotoPickerLoadState.dataLoaded, data)\n            } catch (e: Throwable) {\n                _photoPickerDataFlow.value = QMUIPhotoPickerData(QMUIPhotoPickerLoadState.dataLoaded, null, e)\n            }\n        }\n    }\n\n    fun toggleOrigin(toOpen: Boolean) {\n        _isOriginOpenFlow.value = toOpen\n    }\n\n    fun togglePick(item: QMUIMediaPhotoVO) {\n        if (_photoPickerDataFlow.value.state != QMUIPhotoPickerLoadState.dataLoaded) {\n            QMUILog.w(TAG, \"pick when data is not finish loaded, please check why this method called here?\")\n            return\n        }\n        val list = arrayListOf<Long>()\n        list.addAll(_pickedListFlow.value)\n        if (list.contains(item.model.id)) {\n            _pickedMap.remove(item.model.id)\n            list.remove(item.model.id)\n            _pickedListFlow.value = list\n            _pickedCountFlow.value = list.size\n        } else {\n            if (list.size >= pickLimitCount) {\n                QMUILog.w(TAG, \"can not pick more photo, please check why this method called here?\")\n                return\n            }\n            _pickedMap[item.model.id] = item\n            list.add(item.model.id)\n            _pickedListFlow.value = list\n            _pickedCountFlow.value = list.size\n        }\n    }\n\n    fun getPickedVOList(): List<QMUIMediaPhotoVO>{\n        return _pickedListFlow.value.mapNotNull { id ->\n            _pickedMap[id]\n        }\n    }\n\n    fun getPickedResultList(): List<QMUIPhotoPickItemInfo> {\n        return _pickedListFlow.value.mapNotNull { id ->\n            _pickedMap[id]?.model?.let {\n                QMUIPhotoPickItemInfo(it.id, it.name, it.width, it.height, it.uri, it.rotation)\n            }\n        }\n    }\n}\n\nopen class QMUIPhotoPickerScene\n\nobject QMUIPhotoPickerGridScene : QMUIPhotoPickerScene()\n\nclass QMUIPhotoPickerPreviewScene(\n    val buckedId: String,\n    val onlySelected: Boolean,\n    val currentId: Long\n) : QMUIPhotoPickerScene()\n\nclass QMUIPhotoPickerEditScene(\n    val current: QMUIMediaPhotoVO\n) : QMUIPhotoPickerScene()\n\n\nenum class QMUIPhotoPickerLoadState {\n    permissionChecking, permissionDenied, dataLoading, dataLoaded\n}\n\nclass QMUIPhotoPickerData(\n    val state: QMUIPhotoPickerLoadState,\n    val data: List<QMUIMediaPhotoBucketVO>?,\n    val error: Throwable? = null\n)"
  },
  {
    "path": "photo/src/main/res/anim/scale_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:background=\"@android:color/transparent\">\n\n    <scale\n        android:duration=\"300\"\n        android:fromXScale=\"0.9\"\n        android:fromYScale=\"0.9\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"1.0\"\n        android:toYScale=\"1.0\" />\n\n    <alpha\n        android:duration=\"300\"\n        android:fromAlpha=\"0.0\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:toAlpha=\"1.0\" />\n\n</set>"
  },
  {
    "path": "photo/src/main/res/anim/scale_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:background=\"@android:color/transparent\">\n\n    <scale\n        android:duration=\"300\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"0.8\"\n        android:toYScale=\"0.8\" />\n\n    <alpha\n        android:duration=\"300\"\n        android:fromAlpha=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quad\"\n        android:toAlpha=\"0.0\" />\n\n</set>"
  },
  {
    "path": "photo/src/test/java/com/qmuiteam/ExampleUnitTest.kt",
    "content": "package com.qmuiteam\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n\n}"
  },
  {
    "path": "photo-coil/.gitignore",
    "content": "/build"
  },
  {
    "path": "photo-coil/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.photoVer\n\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildFeatures {\n        compose = true\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n        freeCompilerArgs += \"-Xjvm-default=all\"\n    }\n}\n\ndependencies {\n    implementation(project(\":compose-core\"))\n    implementation(Dep.AndroidX.coreKtx)\n    api(project(\":photo\"))\n    api(Dep.Coil.compose)\n}"
  },
  {
    "path": "photo-coil/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "photo-coil/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "photo-coil/src/androidTest/java/com/qmuiteam/ExampleInstrumentedTest.kt",
    "content": "package com.qmuiteam\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.qmuiteam.test\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "photo-coil/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qmuiteam.photo.coil\">\n\n</manifest>"
  },
  {
    "path": "photo-coil/src/main/java/com/qmuiteam/photo/coil/QMUICoilImageDecoderFactory.kt",
    "content": "package com.qmuiteam.photo.coil\n\nimport android.graphics.BitmapFactory\nimport android.graphics.drawable.BitmapDrawable\nimport androidx.compose.ui.unit.IntSize\nimport coil.ImageLoader\nimport coil.decode.BitmapFactoryDecoder\nimport coil.decode.DecodeResult\nimport coil.decode.Decoder\nimport coil.decode.ImageSource\nimport coil.fetch.SourceResult\nimport coil.request.Options\nimport coil.request.get\nimport coil.size.Scale\nimport coil.size.pxOrElse\nimport com.qmuiteam.photo.data.QMUIBitmapRegionHolderDrawable\nimport com.qmuiteam.photo.data.loadLongImage\nimport com.qmuiteam.photo.data.loadLongImageThumbnail\nimport kotlinx.coroutines.runInterruptible\nimport kotlinx.coroutines.sync.Semaphore\nimport kotlinx.coroutines.sync.withPermit\n\nclass QMUICoilImageDecoderFactory(maxParallelism: Int = 4) : Decoder.Factory {\n\n    companion object {\n        val defaultInstance by lazy {\n            QMUICoilImageDecoderFactory()\n        }\n    }\n\n    private val parallelismLock = Semaphore(maxParallelism)\n\n    override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? {\n        return if ((options.parameters[\"isLongImage\"] as? Boolean) == true) {\n            QMUICoilLongImageDecoder(result.source, options, parallelismLock)\n        } else {\n            BitmapFactoryDecoder(result.source, options, parallelismLock)\n        }\n    }\n}\n\n\nclass QMUICoilLongImageDecoder(\n    private val source: ImageSource,\n    private val options: Options,\n    private val parallelismLock: Semaphore = Semaphore(Int.MAX_VALUE)\n) : Decoder {\n\n    private val isThumb = options.parameters[\"isThumb\"] == true\n\n    override suspend fun decode(): DecodeResult = parallelismLock.withPermit {\n        runInterruptible { decode(BitmapFactory.Options()) }\n    }\n\n\n    private fun decode(bmOptions: BitmapFactory.Options): DecodeResult {\n        val ins = source.source().inputStream()\n        val (width, height) = options.size\n        val dstWidth = width.pxOrElse { -1 }\n        val dstHeight = height.pxOrElse { -1 }\n        if (isThumb) {\n            val bm = loadLongImageThumbnail(ins, IntSize(dstWidth, dstHeight), bmOptions, options.scale == Scale.FIT)\n            return DecodeResult(\n                drawable = BitmapDrawable(options.context.resources, bm),\n                isSampled = bmOptions.inSampleSize > 1\n            )\n        } else {\n            val bitmapRegion = loadLongImage(\n                ins,\n                IntSize(dstWidth, dstHeight),\n                bmOptions,\n                options.scale == Scale.FIT,\n                preloadCount = 2\n            )\n            return DecodeResult(\n                drawable = QMUIBitmapRegionHolderDrawable(bitmapRegion),\n                isSampled = bmOptions.inSampleSize > 1 || bmOptions.inScaled\n            )\n        }\n    }\n}"
  },
  {
    "path": "photo-coil/src/main/java/com/qmuiteam/photo/coil/QMUICoilPhoto.kt",
    "content": "package com.qmuiteam.photo.coil\n\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport android.os.Bundle\nimport android.util.Log\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxWithConstraints\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.graphics.painter.BitmapPainter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.core.graphics.drawable.toBitmap\nimport coil.compose.AsyncImage\nimport coil.compose.AsyncImageContent\nimport coil.compose.AsyncImagePainter\nimport coil.imageLoader\nimport coil.request.ErrorResult\nimport coil.request.ImageRequest\nimport coil.request.SuccessResult\nimport coil.size.Scale\nimport com.qmuiteam.photo.compose.BlankBox\nimport com.qmuiteam.photo.compose.QMUIBitmapRegionItem\nimport com.qmuiteam.photo.data.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nopen class QMUICoilThumbPhoto(\n    val uri: Uri,\n    val isLongImage: Boolean,\n    val openBlankColor: Boolean\n) : QMUIPhoto {\n    @Composable\n    override fun Compose(\n        contentScale: ContentScale,\n        isContainerDimenExactly: Boolean,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    ) {\n        if (isLongImage) {\n            LongImage(onSuccess, onError, openBlankColor)\n        } else {\n            val context = LocalContext.current\n            val model = remember(context, uri, onSuccess, onError) {\n                ImageRequest.Builder(context)\n                    .data(uri)\n                    .allowHardware(false)\n                    .crossfade(true)\n                    .decoderFactory(QMUICoilImageDecoderFactory.defaultInstance)\n                    .listener(onError = { _, result ->\n                        onError?.invoke(result.throwable)\n                    }) { _, result ->\n                        onSuccess?.invoke(PhotoResult(uri, result.drawable))\n                    }.build()\n            }\n            AsyncImage(\n                model = model,\n                contentDescription = \"\",\n                contentScale = if (isContainerDimenExactly) contentScale else ContentScale.Inside,\n                alignment = Alignment.Center,\n                modifier = Modifier.let {\n                    if (isContainerDimenExactly) {\n                        it.fillMaxSize()\n                    } else {\n                        it\n                    }\n                }\n            ) { state ->\n                if (state == AsyncImagePainter.State.Empty || state is AsyncImagePainter.State.Loading) {\n                    if (isContainerDimenExactly && openBlankColor) {\n                        BlankBox()\n                    }\n                } else {\n                    AsyncImageContent()\n                }\n            }\n        }\n\n    }\n\n    @Composable\n    fun LongImage(\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?,\n        openBlankColor: Boolean\n    ) {\n        BoxWithConstraints(Modifier.fillMaxSize()) {\n            val request = ImageRequest.Builder(LocalContext.current)\n                .allowHardware(false)\n                .setParameter(\"isThumb\", true)\n                .setParameter(\"isLongImage\", true)\n                .crossfade(true)\n                .decoderFactory(QMUICoilImageDecoderFactory.defaultInstance)\n                .data(uri)\n                .scale(Scale.FILL)\n                .size(constraints.maxWidth, constraints.maxHeight)\n                .build()\n            LongImageContent(request, onSuccess, onError, openBlankColor)\n        }\n\n    }\n\n    @Composable\n    fun LongImageContent(\n        request: ImageRequest,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?,\n        openBlankColor: Boolean\n    ) {\n        val imageLoader = LocalContext.current.imageLoader\n        var bitmap by remember(\"\") {\n            mutableStateOf<Bitmap?>(null)\n        }\n        LaunchedEffect(\"\") {\n            withContext(Dispatchers.IO) {\n                val result = imageLoader.execute(request)\n                if (result is SuccessResult) {\n                    bitmap = result.drawable.toBitmap()\n                    withContext(Dispatchers.Main) {\n                        onSuccess?.invoke(PhotoResult(uri, result.drawable))\n                    }\n                } else if (result is ErrorResult) {\n                    withContext(Dispatchers.Main) {\n                        onError?.invoke(result.throwable)\n                    }\n                }\n            }\n        }\n        val bm = bitmap\n        if (bm != null) {\n            Image(\n                painter = BitmapPainter(bm.asImageBitmap()),\n                contentDescription = \"\",\n                contentScale = ContentScale.FillWidth,\n                alignment = Alignment.TopCenter,\n                modifier = Modifier.fillMaxSize()\n            )\n        } else if (openBlankColor) {\n            BlankBox()\n        }\n\n    }\n}\n\n\nclass QMUICoilPhoto(\n    val uri: Uri,\n    val isLongImage: Boolean\n) : QMUIPhoto {\n\n    @Composable\n    override fun Compose(\n        contentScale: ContentScale,\n        isContainerDimenExactly: Boolean,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    ) {\n        if (isLongImage) {\n            LongImage(onSuccess, onError)\n        } else {\n            val context = LocalContext.current\n            val model = remember(context, uri, onSuccess, onError) {\n                ImageRequest.Builder(context)\n                    .data(uri)\n                    .allowHardware(false)\n                    .crossfade(true)\n                    .decoderFactory(QMUICoilImageDecoderFactory.defaultInstance)\n                    .listener(onError = { _, result ->\n                        onError?.invoke(result.throwable)\n                    }) { _, result ->\n                        onSuccess?.invoke(PhotoResult(uri, result.drawable))\n                    }.build()\n            }\n            AsyncImage(\n                model = model,\n                contentDescription = \"\",\n                contentScale = contentScale,\n                alignment = Alignment.Center,\n                modifier = Modifier.let {\n                    if (isContainerDimenExactly) {\n                        it.fillMaxSize()\n                    } else {\n                        it\n                    }\n                }\n            )\n        }\n    }\n\n    @Composable\n    fun LongImage(\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    ) {\n        var images by remember {\n            mutableStateOf(emptyList<QMUIBitmapRegionProvider>())\n        }\n        val context = LocalContext.current\n        LaunchedEffect(key1 = \"\") {\n            val result = withContext(Dispatchers.IO) {\n                val request = ImageRequest.Builder(context)\n                    .data(uri)\n                    .crossfade(true)\n                    .setParameter(\"isLongImage\", true)\n                    .decoderFactory(QMUICoilImageDecoderFactory.defaultInstance)\n                    .build()\n                context.imageLoader.execute(request)\n            }\n            if (result is SuccessResult) {\n                (result.drawable as? QMUIBitmapRegionHolderDrawable)?.bitmapRegion?.let {\n                    images = it.list\n                }\n                onSuccess?.invoke(PhotoResult(uri, result.drawable))\n            } else if (result is ErrorResult) {\n                onError?.invoke(result.throwable)\n            }\n        }\n        if (images.isNotEmpty()) {\n            LazyColumn(modifier = Modifier.fillMaxSize()) {\n                items(images) { image ->\n                    BoxWithConstraints() {\n                        val width = constraints.maxWidth\n                        val height = width * image.height / image.width\n                        val heightDp = with(LocalDensity.current) {\n                            height.toDp()\n                        }\n                        QMUIBitmapRegionItem(image, maxWidth, heightDp)\n                    }\n                }\n            }\n        }\n    }\n}\n\n\nopen class QMUICoilPhotoProvider(\n    val uri: Uri,\n    val thumbUri: Uri,\n    val ratio: Float\n) : QMUIPhotoProvider {\n\n    companion object {\n        const val META_URI_KEY = \"meta_uri\"\n        const val META_THUMB_URI_KEY = \"meta_thumb_uri\"\n        const val META_RATIO_KEY = \"meta_ratio\"\n    }\n\n    constructor(uri: Uri, ratio: Float) : this(uri, uri, ratio)\n\n\n    override fun thumbnail(openBlankColor: Boolean): QMUIPhoto? {\n        return QMUICoilThumbPhoto(thumbUri, isLongImage(), openBlankColor)\n    }\n\n    override fun photo(): QMUIPhoto? {\n        return QMUICoilPhoto(uri, isLongImage())\n    }\n\n    override fun ratio(): Float {\n        return ratio\n    }\n\n    override fun isLongImage(): Boolean {\n        return ratio > 0 && ratio < 0.2f\n    }\n\n    override fun meta(): Bundle? {\n        return Bundle().apply {\n            putParcelable(META_URI_KEY, uri)\n            if(thumbUri != uri){\n                putParcelable(META_THUMB_URI_KEY, thumbUri)\n            }\n            putParcelable(META_THUMB_URI_KEY, thumbUri)\n            putFloat(META_RATIO_KEY, ratio)\n        }\n    }\n\n    override fun recoverCls(): Class<out PhotoTransitionProviderRecover>? {\n        return QMUICoilPhotoTransitionProviderRecover::class.java\n    }\n}\n\nclass QMUICoilPhotoTransitionProviderRecover : PhotoTransitionProviderRecover {\n    override fun recover(bundle: Bundle): QMUIPhotoTransitionInfo? {\n        val uri = bundle.getParcelable<Uri>(QMUICoilPhotoProvider.META_URI_KEY) ?: return null\n        val thumbUri = bundle.getParcelable<Uri>(QMUICoilPhotoProvider.META_THUMB_URI_KEY) ?: uri\n        val ratio = bundle.getFloat(QMUICoilPhotoProvider.META_RATIO_KEY)\n        return QMUIPhotoTransitionInfo(\n            QMUICoilPhotoProvider(uri, thumbUri, ratio),\n            null,\n            null,\n            null\n        )\n    }\n}"
  },
  {
    "path": "photo-coil/src/main/java/com/qmuiteam/photo/coil/QMUIMediaCoilPhotoProviderFactory.kt",
    "content": "package com.qmuiteam.photo.coil\n\nimport com.qmuiteam.photo.data.QMUIMediaModel\nimport com.qmuiteam.photo.data.QMUIMediaPhotoProviderFactory\nimport com.qmuiteam.photo.data.QMUIPhotoProvider\n\nclass QMUIMediaCoilPhotoProviderFactory : QMUIMediaPhotoProviderFactory {\n\n    override fun factory(model: QMUIMediaModel): QMUIPhotoProvider {\n        return QMUICoilPhotoProvider(\n            model.uri,\n            model.ratio()\n        )\n    }\n}"
  },
  {
    "path": "photo-coil/src/test/java/com/qmuiteam/ExampleUnitTest.kt",
    "content": "package com.qmuiteam\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n\n}"
  },
  {
    "path": "photo-glide/.gitignore",
    "content": "/build"
  },
  {
    "path": "photo-glide/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    kotlin(\"kapt\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.photoVer\n\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildFeatures {\n        compose = true\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n        freeCompilerArgs += \"-Xjvm-default=all\"\n    }\n}\n\ndependencies {\n    implementation(project(\":compose-core\"))\n    implementation(Dep.AndroidX.coreKtx)\n    api(project(\":photo\"))\n    api(Dep.Glide.glide)\n    kapt(Dep.Glide.compiler)\n}"
  },
  {
    "path": "photo-glide/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "photo-glide/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\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# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "photo-glide/src/androidTest/java/com/qmuiteam/ExampleInstrumentedTest.kt",
    "content": "package com.qmuiteam\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.qmuiteam.test\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "photo-glide/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qmuiteam.photo.glide\">\n\n</manifest>"
  },
  {
    "path": "photo-glide/src/main/java/com/qmuiteam/photo/glide/QMUIGlideModule.kt",
    "content": "package com.qmuiteam.photo.glide\n\nimport android.content.Context\nimport android.graphics.BitmapFactory\nimport android.graphics.drawable.BitmapDrawable\nimport android.graphics.drawable.Drawable\nimport androidx.compose.ui.unit.IntSize\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.Registry\nimport com.bumptech.glide.annotation.GlideModule\nimport com.bumptech.glide.load.Option\nimport com.bumptech.glide.load.Options\nimport com.bumptech.glide.load.ResourceDecoder\nimport com.bumptech.glide.load.engine.Resource\nimport com.bumptech.glide.load.resource.SimpleResource\nimport com.bumptech.glide.load.resource.bitmap.DownsampleStrategy\nimport com.bumptech.glide.module.LibraryGlideModule\nimport com.qmuiteam.photo.data.QMUIBitmapRegionHolderDrawable\nimport com.qmuiteam.photo.data.loadLongImage\nimport com.qmuiteam.photo.data.loadLongImageThumbnail\nimport java.io.IOException\nimport java.io.InputStream\nimport java.nio.ByteBuffer\n\n\nval QMUI_PHOTO_IMG_IS_THUMB = Option.memory(\"com.qmuiteam.photo.isThumb\", false)\n\n\nclass QMUILongGlidePhotoData(\n    val drawable: Drawable\n)\n\n@GlideModule\nclass QMUIGlideModule : LibraryGlideModule() {\n\n    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {\n        registry.prepend(\n            Registry.BUCKET_BITMAP,\n            InputStream::class.java,\n            QMUILongGlidePhotoData::class.java,\n            object : ResourceDecoder<InputStream, QMUILongGlidePhotoData> {\n                override fun handles(source: InputStream, options: Options): Boolean {\n                    return true\n                }\n\n                override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<QMUILongGlidePhotoData> {\n                    return doDecode(context, source, width, height, options)\n                }\n\n            })\n    }\n}\n\nprivate fun doDecode(\n    context: Context,\n    source: InputStream,\n    width: Int,\n    height: Int,\n    options: Options\n): Resource<QMUILongGlidePhotoData> {\n    val isThumb = options.get(QMUI_PHOTO_IMG_IS_THUMB) == true\n    val bmOptions = BitmapFactory.Options()\n    if (isThumb) {\n        val bm = loadLongImageThumbnail(\n            source,\n            IntSize(width, height),\n            bmOptions,\n            options.get(DownsampleStrategy.OPTION) == DownsampleStrategy.CENTER_INSIDE\n        )\n        val drawable = BitmapDrawable(context.resources, bm)\n        return SimpleResource(QMUILongGlidePhotoData(drawable))\n    } else {\n        val bitmapRegion = loadLongImage(\n            source,\n            IntSize(width, height),\n            bmOptions,\n            options.get(DownsampleStrategy.OPTION) == DownsampleStrategy.CENTER_INSIDE,\n            preloadCount = 2\n        )\n        val drawable = QMUIBitmapRegionHolderDrawable(bitmapRegion)\n        return SimpleResource(QMUILongGlidePhotoData(drawable))\n    }\n}\n\nprivate class ByteBufferInputStream(val buf: ByteBuffer) : InputStream() {\n    @Throws(IOException::class)\n    override fun read(): Int {\n        return if (!buf.hasRemaining()) {\n            -1\n        } else buf.get().toInt() and 0xFF\n    }\n\n    @Throws(IOException::class)\n    override fun read(bytes: ByteArray, off: Int, len: Int): Int {\n        if (!buf.hasRemaining()) {\n            return -1\n        }\n        val toRead = len.coerceAtMost(buf.remaining())\n        buf.get(bytes, off, toRead)\n        return toRead\n    }\n}"
  },
  {
    "path": "photo-glide/src/main/java/com/qmuiteam/photo/glide/QMUIGlidePhoto.kt",
    "content": "package com.qmuiteam.photo.glide\n\nimport android.graphics.drawable.Drawable\nimport android.net.Uri\nimport android.os.Bundle\nimport android.os.SystemClock\nimport android.util.Log\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.BoxWithConstraints\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.graphics.painter.BitmapPainter\nimport androidx.compose.ui.layout.ContentScale\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.core.graphics.drawable.toBitmap\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.load.resource.bitmap.DownsampleStrategy\nimport com.bumptech.glide.request.target.CustomTarget\nimport com.bumptech.glide.request.transition.Transition\nimport com.qmuiteam.photo.compose.BlankBox\nimport com.qmuiteam.photo.compose.QMUIBitmapRegionItem\nimport com.qmuiteam.photo.compose.QMUILocalPhotoConfig\nimport com.qmuiteam.photo.data.*\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\n\n\n@Composable\nprivate fun GlideImage(\n    uri: Uri,\n    isLongImage: Boolean,\n    isThumbImage: Boolean,\n    isContainerDimenExactly: Boolean,\n    onSuccess: ((PhotoResult) -> Unit)?,\n    onError: (() -> Unit)?,\n    contentDescription: String = \"\",\n    contentScale: ContentScale = ContentScale.Fit,\n    openBlankColor: Boolean = false\n) {\n    BoxWithConstraints(modifier = if (isContainerDimenExactly) Modifier.fillMaxSize() else Modifier) {\n        val state = remember(uri) {\n            mutableStateOf<Pair<Long, Drawable?>?>(null)\n        }\n        val context = LocalContext.current\n        Log.i(\"cginetest\", \"1. $constraints\")\n        DisposableEffect(uri, isContainerDimenExactly, constraints.isZero,isLongImage, isThumbImage, contentScale) {\n            val key = SystemClock.elapsedRealtime()\n            val request = when {\n                constraints.isZero ->  null\n                isLongImage -> {\n                    Glide.with(context).`as`(QMUILongGlidePhotoData::class.java).load(uri)\n                        .downsample(DownsampleStrategy.CENTER_OUTSIDE)\n                        .dontTransform()\n                        .set(QMUI_PHOTO_IMG_IS_THUMB, isThumbImage)\n                        .into(object : CustomTarget<QMUILongGlidePhotoData>(\n                            constraints.maxWidth,\n                            constraints.maxHeight\n                        ) {\n\n                            override fun onResourceReady(resource: QMUILongGlidePhotoData, transition: Transition<in QMUILongGlidePhotoData>?) {\n                                state.value = key to resource.drawable\n                                onSuccess?.invoke(PhotoResult(uri, resource.drawable))\n                            }\n\n\n                            override fun onLoadStarted(placeholder: Drawable?) {\n                                if (placeholder != null || state.value?.first == key) {\n                                    state.value = -1L to placeholder\n                                }\n                            }\n\n                            override fun onLoadCleared(placeholder: Drawable?) {\n                                if (state.value?.first == key) {\n                                    state.value = -1L to placeholder\n                                }\n                            }\n\n                            override fun onLoadFailed(errorDrawable: Drawable?) {\n                                onError?.invoke()\n                            }\n                        })\n                        .request\n                }\n                else -> {\n                    Glide.with(context).load(uri)\n                        .downsample(DownsampleStrategy.AT_LEAST)\n                        .dontTransform()\n                        .into(object : CustomTarget<Drawable>(\n                            constraints.maxWidth,\n                            constraints.maxHeight\n                        ) {\n\n                            override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {\n                                state.value = key to resource\n                                onSuccess?.invoke(PhotoResult(uri, resource))\n                            }\n\n\n                            override fun onLoadStarted(placeholder: Drawable?) {\n                                if (placeholder != null || state.value?.first == key) {\n                                    state.value = -1L to placeholder\n                                }\n                            }\n\n                            override fun onLoadCleared(placeholder: Drawable?) {\n                                if (state.value?.first == key) {\n                                    state.value = -1L to placeholder\n                                }\n                            }\n\n                            override fun onLoadFailed(errorDrawable: Drawable?) {\n                                onError?.invoke()\n                            }\n                        })\n                        .request\n                }\n            }\n\n            onDispose {\n                request?.clear()\n            }\n        }\n        val currentDrawable = state.value?.second\n        if (currentDrawable != null) {\n            if (currentDrawable is QMUIBitmapRegionHolderDrawable) {\n                LongImageContent(currentDrawable)\n            } else {\n                Image(\n                    modifier = if (isContainerDimenExactly) {\n                        Modifier.fillMaxSize()\n                    } else Modifier,\n                    contentDescription = contentDescription,\n                    painter = BitmapPainter(currentDrawable.toBitmap().asImageBitmap()),\n                    contentScale = contentScale,\n                )\n            }\n\n        } else if (isContainerDimenExactly && openBlankColor) {\n            BlankBox()\n        }\n    }\n}\n\n@Composable\nprivate fun LongImageContent(drawable: QMUIBitmapRegionHolderDrawable) {\n    val images by remember(drawable) {\n        mutableStateOf(drawable.bitmapRegion.list)\n    }\n    if (images.isNotEmpty()) {\n        LazyColumn(modifier = Modifier.fillMaxSize()) {\n            items(images) { image ->\n                BoxWithConstraints() {\n                    val width = constraints.maxWidth\n                    val height = width * image.height / image.width\n                    val heightDp = with(LocalDensity.current) {\n                        height.toDp()\n                    }\n                    QMUIBitmapRegionItem(image, maxWidth, heightDp)\n                }\n            }\n        }\n    }\n}\n\nopen class QMUIGlideThumbPhoto(\n    val uri: Uri,\n    val isLongImage: Boolean,\n    val openBlankColor: Boolean = true,\n) : QMUIPhoto {\n    @Composable\n    override fun Compose(\n        contentScale: ContentScale,\n        isContainerDimenExactly: Boolean,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    ) {\n        GlideImage(\n            uri,\n            isLongImage,\n            true,\n            isContainerDimenExactly,\n            onSuccess,\n            onError = {\n                onError?.invoke(RuntimeException(\"glide failed to load thumb image.\"))\n            },\n            contentScale = contentScale,\n            openBlankColor = openBlankColor\n        )\n    }\n}\n\n\nclass QMUIGlidePhoto(\n    val uri: Uri,\n    val isLongImage: Boolean\n) : QMUIPhoto {\n\n    @Composable\n    override fun Compose(\n        contentScale: ContentScale,\n        isContainerDimenExactly: Boolean,\n        onSuccess: ((PhotoResult) -> Unit)?,\n        onError: ((Throwable) -> Unit)?\n    ) {\n        GlideImage(\n            uri,\n            isLongImage,\n            false,\n            isContainerDimenExactly,\n            onSuccess,\n            onError = {\n                onError?.invoke(RuntimeException(\"glide failed to load thumb image.\"))\n            },\n            contentScale = contentScale\n        )\n    }\n}\n\nopen class QMUIGlidePhotoProvider(val uri: Uri, val thumbUrl: Uri, val ratio: Float) : QMUIPhotoProvider {\n\n    companion object {\n        const val META_URI_KEY = \"meta_uri\"\n        const val META_THUMB_URI_KEY = \"meta_thumb_uri\"\n        const val META_RATIO_KEY = \"meta_ratio\"\n    }\n\n    constructor(uri: Uri, ratio: Float): this(uri, uri, ratio)\n\n    override fun thumbnail(openBlankColor: Boolean): QMUIPhoto? {\n        return QMUIGlideThumbPhoto(thumbUrl, isLongImage(), openBlankColor)\n    }\n\n    override fun photo(): QMUIPhoto? {\n        return QMUIGlidePhoto(uri, isLongImage())\n    }\n\n    override fun ratio(): Float {\n        return ratio\n    }\n\n    override fun isLongImage(): Boolean {\n        return ratio > 0 && ratio < 0.2f\n    }\n\n    override fun meta(): Bundle? {\n        return Bundle().apply {\n            putParcelable(META_URI_KEY, uri)\n            if(thumbUrl != uri){\n                putParcelable(META_THUMB_URI_KEY, thumbUrl)\n            }\n            putFloat(META_RATIO_KEY, ratio)\n        }\n    }\n\n    override fun recoverCls(): Class<out PhotoTransitionProviderRecover>? {\n        return QMUIGlidePhotoTransitionProviderRecover::class.java\n    }\n}\n\nclass QMUIGlidePhotoTransitionProviderRecover : PhotoTransitionProviderRecover {\n    override fun recover(bundle: Bundle): QMUIPhotoTransitionInfo? {\n        val uri = bundle.getParcelable<Uri>(QMUIGlidePhotoProvider.META_URI_KEY) ?: return null\n        val thumbUri = bundle.getParcelable<Uri>(QMUIGlidePhotoProvider.META_THUMB_URI_KEY) ?: uri\n        val ratio = bundle.getFloat(QMUIGlidePhotoProvider.META_RATIO_KEY)\n        return QMUIPhotoTransitionInfo(\n            QMUIGlidePhotoProvider(uri, thumbUri, ratio),\n            null,\n            null,\n            null\n        )\n    }\n}"
  },
  {
    "path": "photo-glide/src/main/java/com/qmuiteam/photo/glide/QMUIMediaGlidePhotoProviderFactory.kt",
    "content": "package com.qmuiteam.photo.glide\n\nimport com.qmuiteam.photo.data.QMUIMediaModel\nimport com.qmuiteam.photo.data.QMUIMediaPhotoProviderFactory\nimport com.qmuiteam.photo.data.QMUIPhotoProvider\n\nclass QMUIMediaGlidePhotoProviderFactory : QMUIMediaPhotoProviderFactory {\n\n    override fun factory(model: QMUIMediaModel): QMUIPhotoProvider {\n        return QMUIGlidePhotoProvider(\n            model.uri,\n            model.ratio()\n        )\n    }\n}"
  },
  {
    "path": "photo-glide/src/test/java/com/qmuiteam/ExampleUnitTest.kt",
    "content": "package com.qmuiteam\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n\n}"
  },
  {
    "path": "plugin/.gitignore",
    "content": "/build\n*.iml\n.DS_Store\n.gradle\n.gradletasknamecache\n.idea"
  },
  {
    "path": "plugin/build.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    `java-gradle-plugin`\n    idea\n    kotlin(\"jvm\") version \"1.6.20\"\n    `kotlin-dsl`\n}\n\nbuildscript {\n    repositories {\n        mavenCentral()\n        google()\n        mavenLocal()\n    }\n    dependencies {\n        classpath(\"com.android.tools.build:gradle:7.1.3\")\n        classpath(\"org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20\")\n    }\n}\n\ngroup = \"com.qmuiteam.qmui.plugin\"\nversion = \"0.0.1\"\n\n\ngradlePlugin {\n    plugins {\n        create(\"qmui-dep\"){\n            id = \"qmui-dep\"\n            implementationClass = \"com.qmuiteam.plugin.QMUIDepPlugin\"\n        }\n\n        create(\"qmui-publish\"){\n            id = \"qmui-publish\"\n            implementationClass = \"com.qmuiteam.plugin.QMUIPublish\"\n        }\n    }\n}\n\nrepositories {\n    mavenCentral()\n    google()\n}\n\ndependencies {\n    api(gradleApi())\n    api(gradleKotlinDsl())\n    api(kotlin(\"gradle-plugin\", version = \"1.6.20\"))\n    api(kotlin(\"gradle-plugin-api\", version = \"1.6.20\"))\n    api(\"com.android.tools.build:gradle-api:7.1.3\")\n    api(\"com.android.tools.build:gradle:7.1.3\")\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_11\n    targetCompatibility = JavaVersion.VERSION_11\n}\n\nval compileKotlin: KotlinCompile by tasks\ncompileKotlin.kotlinOptions {\n    jvmTarget = \"11\"\n}"
  },
  {
    "path": "plugin/settings.gradle.kts",
    "content": ""
  },
  {
    "path": "plugin/src/main/java/com/qmuiteam/plugin/Dep.kt",
    "content": "package com.qmuiteam.plugin\n\nimport org.gradle.api.JavaVersion\n\nobject Dep {\n\n    val javaVersion = JavaVersion.VERSION_11\n    const val kotlinJvmTarget = \"11\"\n    const val compileSdk = 31\n    const val minSdk = 21\n    const val targetSdk = 31\n\n\n    object QMUI {\n        const val group = \"com.qmuiteam\"\n        const val qmuiVer = \"2.1.0.4\"\n        const val archVer = \"2.1.0.3\"\n        const val typeVer = \"0.1.0.5\"\n\n        // composeMajor.composeMinor.qmuiReleaseNumber\n        const val composeCoreVer = \"1.1.1\"\n        const val composeVer = \"1.1.1\"\n        const val photoVer = \"1.1.1.1\"\n        const val editorVer = \"1.1.1\"\n    }\n\n    object Coroutines {\n        private const val version = \"1.6.0\"\n        const val core = \"org.jetbrains.kotlinx:kotlinx-coroutines-core:$version\"\n        const val android = \"org.jetbrains.kotlinx:kotlinx-coroutines-android:$version\"\n        const val test = \"org.jetbrains.kotlinx:kotlinx-coroutines-test:$version\"\n    }\n\n    object AndroidX {\n        val appcompat = \"androidx.appcompat:appcompat:1.4.0\"\n        val annotation = \"androidx.annotation:annotation:1.3.0\"\n        val coreKtx = \"androidx.core:core-ktx:1.7.0\"\n        val constraintLayout = \"androidx.constraintlayout:constraintlayout:2.1.2\"\n        val swiperefreshlayout = \"androidx.swiperefreshlayout:swiperefreshlayout:1.1.0\"\n        val activity = \"androidx.activity:activity-ktx:1.4.0\"\n        val fragment = \"androidx.fragment:fragment:1.4.1\"\n    }\n\n    object Compose {\n        val version = \"1.2.0-alpha08\"\n        val animation = \"androidx.compose.animation:animation:$version\"\n        val ui = \"androidx.compose.ui:ui:$version\"\n        val material = \"androidx.compose.material:material:$version\"\n        val compiler = \"androidx.compose.compiler:compiler:$version\"\n        val activity = \"androidx.activity:activity-compose:1.4.0\"\n        val constraintlayout = \"androidx.constraintlayout:constraintlayout-compose:1.0.0\"\n\n        val pager = \"com.google.accompanist:accompanist-pager:0.23.1\"\n    }\n\n    object Flipper {\n        private const val version = \"0.96.1\"\n        const val soLoader = \"com.facebook.soloader:soloader:0.10.1\"\n        const val flipper = \"com.facebook.flipper:flipper:$version\"\n    }\n\n    object MaterialDesign {\n        const val material = \"com.google.android.material:material:1.4.0\"\n    }\n\n    object CodeGen {\n        const val javapoet = \"com.squareup:javapoet:1.13.0\"\n        const val autoService = \"com.google.auto.service:auto-service:1.0-rc2\"\n    }\n\n    object ButterKnife {\n        private const val ver = \"10.1.0\"\n        const val butterknife = \"com.jakewharton:butterknife:$ver\"\n        const val compiler = \"com.jakewharton:butterknife-compiler:$ver\"\n    }\n\n    object Coil {\n        const val compose = \"io.coil-kt:coil-compose:2.0.0-alpha06\"\n    }\n\n    object Glide {\n        private const val ver = \"4.13.0\"\n        const val glide = \"com.github.bumptech.glide:glide:$ver\"\n        const val compiler = \"com.github.bumptech.glide:compiler:$ver\"\n    }\n}"
  },
  {
    "path": "plugin/src/main/java/com/qmuiteam/plugin/QMUIDepPlugin.kt",
    "content": "package com.qmuiteam.plugin\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass QMUIDepPlugin: Plugin<Project>{\n    override fun apply(project: Project) {\n\n    }\n}"
  },
  {
    "path": "plugin/src/main/java/com/qmuiteam/plugin/QMUIPublish.kt",
    "content": "package com.qmuiteam.plugin\n\nimport com.android.build.gradle.LibraryExtension\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.api.publish.PublishingExtension\nimport org.gradle.api.publish.maven.MavenPublication\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.create\nimport org.gradle.plugins.signing.SigningExtension\nimport java.io.File\nimport java.util.*\nimport kotlin.io.*\n\nclass QMUIPublish : Plugin<Project> {\n    override fun apply(project: Project) {\n        val isAndroid = project.hasProperty(\"android\")\n\n        if (isAndroid) {\n            println(\"android\")\n            val android = project.extensions.getByName(\"android\") as LibraryExtension\n            android.publishing {\n                singleVariant(\"release\") {\n                    withJavadocJar()\n                    withSourcesJar()\n                }\n            }\n\n        } else {\n            println(\"java/kotlin\")\n            project.configure<JavaPluginExtension> {\n                withSourcesJar()\n                withJavadocJar()\n            }\n        }\n\n        project.afterEvaluate {\n            val properties = Properties()\n            val file = File(project.rootProject.file(\"gradle\"), \"deploy.properties\")\n            if (file.exists()) {\n                properties.load(file.inputStream())\n                val mavenUrl = properties.getProperty(\"maven.url\")\n                val mavenUsername = properties.getProperty(\"maven.username\")\n                val mavenPassword = properties.getProperty(\"maven.password\")\n\n                println(\"mavenUrl:$mavenUrl\")\n\n                project.configure<PublishingExtension> {\n                    repositories {\n                        maven {\n                            setUrl(mavenUrl)\n                            credentials {\n                                username = mavenUsername\n                                password = mavenPassword\n                            }\n                        }\n                    }\n                    publications {\n                        create<MavenPublication>(\"release\") {\n\n                            project.configure<SigningExtension> {\n                                sign(this@create)\n                            }\n\n                            if (isAndroid) {\n                                from(components.getByName(\"release\"))\n                            } else {\n                                from(components.getByName(\"java\"))\n                            }\n\n                            groupId = project.group as String\n                            artifactId = project.name\n                            version = project.version as String\n\n                            pom {\n                                name.set(\"${project.group}:${project.name}\")\n                                url.set(\"https://github.com/Tencent/QMUI_Android\")\n                                description.set(\"qmui android library.\")\n                                licenses {\n                                    license {\n                                        name.set(properties.getProperty(\"license.name\"))\n                                        url.set(properties.getProperty(\"license.url\"))\n                                    }\n                                }\n                                developers {\n                                    developer {\n                                        id.set(properties.getProperty(\"developer.id\"))\n                                        name.set(properties.getProperty(\"developer.name\"))\n                                        email.set(properties.getProperty(\"developer.email\"))\n                                    }\n                                }\n                                scm {\n                                    connection.set(\"scm:git:git://github.com/Tencent/QMUI_Android.git\")\n                                    developerConnection.set(\"scm:git:ssh://github.com/Tencent/QMUI_Android.git\")\n                                    url.set(\"https://qmuiteam.com/android\")\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nfun println(log: String) {\n    kotlin.io.println(\"qmui config publish > $log\")\n}"
  },
  {
    "path": "qmui/.gitignore",
    "content": "/*.bin\n/*.iml\n/.DS_Store\n/.gradletasknamecache\n/.idea\n/bin\n/build\n/local.properties\n"
  },
  {
    "path": "qmui/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.qmuiVer\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n}\n\n\ndependencies {\n    api(Dep.AndroidX.appcompat)\n    api(Dep.AndroidX.annotation)\n    api(Dep.AndroidX.constraintLayout)\n    api(Dep.AndroidX.swiperefreshlayout)\n\n    api(Dep.MaterialDesign.material)\n}"
  },
  {
    "path": "qmui/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/chant/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "qmui/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.qmuiteam.qmui\">\n</manifest>\n"
  },
  {
    "path": "qmui/src/main/assets/QMUIWebviewBridge.js",
    "content": "(function(){\n    var doc = document;\n    if(window.QMUIBridge){\n        return;\n    }\n    var messagingIframe = createIframe(doc);\n    var sendingMessageQueue = [];\n    var receivedMessageQueue = [];\n    var messageHandlers = {};\n    var QUEUE_HAS_MESSAGE = 'qmui://__QUEUE_MESSAGE__/';\n    var responseCallbacks = {};\n    var uuid = 1;\n\n    function createIframe(doc) {\n        var iframe = doc.createElement('iframe');\n        iframe.style.display = 'none';\n        doc.documentElement.appendChild(iframe);\n        return iframe;\n    }\n\n    function send(data, callback) {\n        if(!data){\n            throw new Error(\"message == null\")\n        }\n        var message = {\n            data: data\n        }\n        if(callback){\n            var callbackId = 'cb_' + (uuid++) + '_' + (new Date() - 0);\n            responseCallbacks[callbackId] = callback;\n            message.callbackId = callbackId;\n        }\n        sendingMessageQueue.push(message);\n        messagingIframe.src = QUEUE_HAS_MESSAGE;\n    }\n\n    function isCmdSupport(cmd, callback){\n        if(isCmdSupport.__cache && isCmdSupport.__cache.indexOf(cmd) >= 0){\n            callback(true)\n            return\n        }\n        getSupportedCmdList(function(data){\n            if(data && data.length > 0){\n                if(!isCmdSupport.__cache){\n                    isCmdSupport.__cache = []\n                }\n                for(var i = 0; i < data.length; i++){\n                    isCmdSupport.__cache.push(data[i])\n                }\n            }\n            callback(isCmdSupport.__cache.indexOf(cmd) >= 0)\n        })\n\n    }\n\n    function getSupportedCmdList(callback){\n        if(getSupportedCmdList.__cache){\n            callback(getSupportedCmdList.__cache)\n            return\n        }\n        send({__cmd__: \"getSupportedCmdList\"}, function(data){\n            getSupportedCmdList.__cache = data\n            callback(data)\n        })\n    }\n\n    function _fetchQueueFromNative(){\n        var messageQueueString = JSON.stringify(sendingMessageQueue);\n        sendingMessageQueue = [];\n        return messageQueueString;\n    }\n\n    function _handleResponseFromNative(response){\n        if(response && response.callbackId){\n            var responseCallback = responseCallbacks[response.callbackId];\n            if(responseCallback){\n                responseCallback(response.data);\n                delete responseCallbacks[response.callbackId];\n            }\n        }\n    }\n\n    var QMUIBridge = window.QMUIBridge = {\n        send: send,\n        isCmdSupport: isCmdSupport,\n        getSupportedCmdList: getSupportedCmdList,\n        _fetchQueueFromNative: _fetchQueueFromNative,\n        _handleResponseFromNative: _handleResponseFromNative\n    };\n\n    var readyEvent = doc.createEvent('Events');\n    readyEvent.initEvent('QMUIBridgeReady');\n    readyEvent.bridge = QMUIBridge;\n    doc.dispatchEvent(readyEvent);\n})()"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/Beta.java",
    "content": "package com.qmuiteam.qmui;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.SOURCE)\npublic @interface Beta {\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/QMUIConfig.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui;\n\npublic class QMUIConfig {\n    public static boolean DEBUG = false;\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/QMUIInterpolatorStaticHolder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui;\n\nimport androidx.interpolator.view.animation.FastOutLinearInInterpolator;\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator;\nimport androidx.interpolator.view.animation.LinearOutSlowInInterpolator;\n\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.AccelerateInterpolator;\nimport android.view.animation.DecelerateInterpolator;\nimport android.view.animation.Interpolator;\nimport android.view.animation.LinearInterpolator;\n\n/**\n * @author cginechen\n * @date 2017-09-02\n */\n\npublic class QMUIInterpolatorStaticHolder {\n    public static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();\n    public static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();\n    public static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new FastOutLinearInInterpolator();\n    public static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator();\n    public static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();\n    public static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();\n    public static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();\n    public static final Interpolator QUNITIC_INTERPOLATOR = new Interpolator() {\n        @Override\n        public float getInterpolation(float t) {\n            t -= 1.0f;\n            return t * t * t * t * t + 1.0f;\n        }\n    };\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/QMUILog.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui;\n\n/**\n *\n * @author cginechen\n * @date 2016-08-11\n */\npublic class QMUILog {\n    public interface QMUILogDelegate {\n        void e(final String tag, final String msg, final Object ... obj);\n        void w(final String tag, final String msg, final Object ... obj);\n        void i(final String tag, final String msg, final Object ... obj);\n        void d(final String tag, final String msg, final Object ... obj);\n        void printErrStackTrace(String tag, Throwable tr, final String format, final Object ... obj);\n    }\n\n    private static QMUILogDelegate sDelegete = null;\n\n    public static void setDelegete(QMUILogDelegate delegete) {\n        sDelegete = delegete;\n    }\n\n    public static void e(final String tag, final String msg, final Object ... obj) {\n        if (sDelegete != null) {\n            sDelegete.e(tag, msg, obj);\n        }\n    }\n\n    public static void w(final String tag, final String msg, final Object ... obj) {\n        if (sDelegete != null) {\n            sDelegete.w(tag, msg, obj);\n        }\n    }\n\n    public static void i(final String tag, final String msg, final Object ... obj) {\n        if (sDelegete != null) {\n            sDelegete.i(tag, msg, obj);\n        }\n    }\n\n    public static void d(final String tag, final String msg, final Object ... obj) {\n        if (sDelegete != null) {\n            sDelegete.d(tag, msg, obj);\n        }\n    }\n\n    public static void printErrStackTrace(String tag, Throwable tr, final String format, final Object ... obj) {\n        if (sDelegete != null) {\n            sDelegete.printErrStackTrace(tag, tr, format, obj);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaButton.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport androidx.appcompat.widget.AppCompatButton;\nimport android.util.AttributeSet;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaButton extends AppCompatButton implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaButton(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaButton(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaButton(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaConstraintLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaConstraintLayout extends ConstraintLayout implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaConstraintLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaConstraintLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaFrameLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaFrameLayout extends FrameLayout implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaFrameLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaImageButton.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport androidx.appcompat.widget.AppCompatImageButton;\nimport android.util.AttributeSet;\n\npublic class QMUIAlphaImageButton extends AppCompatImageButton implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaImageButton(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaImageButton(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaImageButton(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaLinearLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaLinearLayout extends LinearLayout implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaLinearLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaLinearLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaRelativeLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.RelativeLayout;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaRelativeLayout extends RelativeLayout implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaRelativeLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaRelativeLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度\n */\npublic class QMUIAlphaTextView extends QMUISpanTouchFixTextView implements QMUIAlphaViewInf {\n\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n\n    public QMUIAlphaTextView(Context context) {\n        super(context);\n    }\n\n    public QMUIAlphaTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIAlphaTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    @Override\n    protected void onSetPressed(boolean pressed) {\n        super.onSetPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    @Override\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaViewHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\nimport androidx.annotation.NonNull;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.lang.ref.WeakReference;\n\npublic class QMUIAlphaViewHelper {\n\n    private WeakReference<View> mTarget;\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     */\n    private boolean mChangeAlphaWhenPress = true;\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     */\n    private boolean mChangeAlphaWhenDisable = true;\n\n    private float mNormalAlpha = 1f;\n    private float mPressedAlpha = .5f;\n    private float mDisabledAlpha = .5f;\n\n    public QMUIAlphaViewHelper(@NonNull View target) {\n        mTarget = new WeakReference<>(target);\n        mPressedAlpha = QMUIResHelper.getAttrFloatValue(target.getContext(), R.attr.qmui_alpha_pressed);\n        mDisabledAlpha = QMUIResHelper.getAttrFloatValue(target.getContext(), R.attr.qmui_alpha_disabled);\n    }\n\n    public QMUIAlphaViewHelper(@NonNull View target, float pressedAlpha, float disabledAlpha) {\n        mTarget = new WeakReference<>(target);\n        mPressedAlpha = pressedAlpha;\n        mDisabledAlpha = disabledAlpha;\n    }\n\n    /**\n     * 在 {@link View#setPressed(boolean)} 中调用，通知 helper 更新\n     * @param current the view to be handled, maybe not equal to target view\n     * @param pressed {@link View#setPressed(boolean)} 中接收到的参数\n     */\n    public void onPressedChanged(View current, boolean pressed) {\n        View target = mTarget.get();\n        if (target == null) {\n            return;\n        }\n        if (current.isEnabled()) {\n            target.setAlpha(mChangeAlphaWhenPress && pressed && current.isClickable() ? mPressedAlpha : mNormalAlpha);\n        } else {\n            if (mChangeAlphaWhenDisable) {\n                target.setAlpha(mDisabledAlpha);\n            }\n        }\n    }\n\n    /**\n     * 在 {@link View#setEnabled(boolean)} 中调用，通知 helper 更新\n     * @param current the view to be handled, maybe not  equal to target view\n     * @param enabled {@link View#setEnabled(boolean)} 中接收到的参数\n     */\n    public void onEnabledChanged(View current, boolean enabled) {\n        View target = mTarget.get();\n        if (target == null) {\n            return;\n        }\n        float alphaForIsEnable;\n        if (mChangeAlphaWhenDisable) {\n            alphaForIsEnable = enabled ? mNormalAlpha : mDisabledAlpha;\n        } else {\n            alphaForIsEnable = mNormalAlpha;\n        }\n        if (current != target && target.isEnabled() != enabled) {\n            target.setEnabled(enabled);\n        }\n        target.setAlpha(alphaForIsEnable);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        mChangeAlphaWhenPress = changeAlphaWhenPress;\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        mChangeAlphaWhenDisable = changeAlphaWhenDisable;\n        View target = mTarget.get();\n        if (target != null) {\n            onEnabledChanged(target, target.isEnabled());\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/alpha/QMUIAlphaViewInf.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.alpha;\n\n/**\n * 在 pressed 和 disabled 时改变 View 的透明度的接口\n */\npublic interface QMUIAlphaViewInf {\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    void setChangeAlphaWhenPress(boolean changeAlphaWhenPress);\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable);\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/exposure/Exposure.kt",
    "content": "package com.qmuiteam.qmui.exposure\n\nimport android.view.View\n\n\nenum class ExposureType {\n    first, dataChange, repeat\n}\n\ninterface Exposure {\n    fun same(data: Exposure): Boolean\n    fun expose(view: View, type: ExposureType)\n}\n\n\n\nclass SimpleExposure(val key: Any?, val block: (type: ExposureType) -> Unit) : Exposure {\n    override fun same(data: Exposure): Boolean {\n        return data is SimpleExposure && data.key == key\n    }\n\n    override fun expose(view: View, type: ExposureType) {\n        block(type)\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/exposure/ExposureChecker.kt",
    "content": "package com.qmuiteam.qmui.exposure\n\nimport android.graphics.Rect\nimport android.view.View\nimport android.view.ViewGroup\nimport com.qmuiteam.qmui.util.QMUIViewHelper\nimport java.util.*\n\nprivate val rect = Rect()\n\ninterface ExposureChecker {\n\n    fun canExpose(target: View): Boolean {\n        return target.defaultCanExpose()\n    }\n\n    fun isExposedInContainer(container: ViewGroup, target: View): Boolean\n}\n\n\nclass FastAreaExposureChecker(val percent: Float) : ExposureChecker {\n    override fun isExposedInContainer(container: ViewGroup, target: View): Boolean {\n        if (target.width <= 0 || target.height <= 0) {\n            return false\n        }\n        QMUIViewHelper.getDescendantRect(container, target, rect)\n        if (rect.left >= container.width || rect.top >= container.height || rect.right <= 0 || rect.bottom <= 0) {\n            return false\n        }\n        if (rect.left < 0) {\n            rect.left = 0\n        }\n        if (rect.right > container.width) {\n            rect.right = container.width\n        }\n        if (rect.top < 0) {\n            rect.top = 0\n        }\n        if (rect.bottom > container.height) {\n            rect.bottom = container.height\n        }\n        return (rect.width() * rect.height() * 1f) / (target.width * target.height) >= percent\n    }\n}\n\nclass AreaExposureChecker(val percent: Float) : ExposureChecker {\n    override fun isExposedInContainer(container: ViewGroup, target: View): Boolean {\n        if (target.width <= 0 || target.height <= 0) {\n            return false\n        }\n        val hasVisibleArea = QMUIViewHelper.getDescendantVisibleRect(container, target, rect)\n        if (!hasVisibleArea) {\n            return false\n        }\n        return (rect.width() * rect.height() * 1f) / (target.width * target.height) >= percent\n    }\n}\n\nval fastFullExposureChecker = FastAreaExposureChecker(1f)\nval fullExposureChecker = AreaExposureChecker(1f)\n\nval defaultExposureChecker = AreaExposureChecker(0.80f)\n\n\nfun interface CustomExposureTriggerListener {\n    fun doCheck()\n}\n\n\nclass CustomExposureTrigger {\n\n    private val listeners = mutableListOf<CustomExposureTriggerListener>()\n    private var isTriggering = false\n    private val pendingActions = LinkedList<PendingAction>()\n\n    fun addListener(listener: CustomExposureTriggerListener) {\n        if (isTriggering) {\n            pendingActions.add(PendingAction(listener, true))\n        } else {\n            listeners.add(listener)\n        }\n\n    }\n\n    fun removeListener(listener: CustomExposureTriggerListener) {\n        if (isTriggering) {\n            pendingActions.add(PendingAction(listener, true))\n        } else {\n            listeners.remove(listener)\n        }\n    }\n\n    fun trigger() {\n        isTriggering = true\n        listeners.forEach {\n            it.doCheck()\n        }\n        isTriggering = false\n        var pendingAction = pendingActions.poll()\n        while (pendingAction != null) {\n            if (pendingAction.isDelete) {\n                removeListener(pendingAction.listener)\n            } else {\n                addListener(pendingAction.listener)\n            }\n            pendingAction = pendingActions.poll()\n        }\n    }\n\n    private class PendingAction(\n        val listener: CustomExposureTriggerListener,\n        val isDelete: Boolean\n    )\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/exposure/ExposureContainer.kt",
    "content": "package com.qmuiteam.qmui.exposure\n\nimport android.view.View\nimport android.view.ViewGroup\n\ninterface ExposureContainerProvider {\n    fun provide(view: View): ViewGroup?\n}\n\nobject DefaultExposureContainerProvider : ExposureContainerProvider {\n    override fun provide(view: View): ViewGroup? {\n        return view.rootView as? ViewGroup\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/exposure/ExposureEffect.kt",
    "content": "package com.qmuiteam.qmui.exposure\n\nimport android.os.SystemClock\nimport android.view.View\nimport android.view.ViewGroup\nimport com.qmuiteam.qmui.R\n\nenum class EffectResult {\n    pass, handled, unHandled\n}\n\ninterface ExposureEffect {\n    fun doBeforeExpose(\n        target: View,\n        container: ViewGroup,\n        exposure: Exposure,\n        lastExposure: Exposure?,\n        type: ExposureType\n    ): EffectResult\n\n    fun doAfterUnExpose(\n        target: View,\n        container: ViewGroup,\n        data: Exposure\n    ){\n\n    }\n}\n\nclass ParentExposedRequestExposureEffect(val parent: ViewGroup) : ExposureEffect {\n    override fun doBeforeExpose(\n        target: View,\n        container: ViewGroup,\n        exposure: Exposure,\n        lastExposure: Exposure?,\n        type: ExposureType\n    ): EffectResult {\n        val isParentConfigSet = parent.getTag(R.id.qmui_exposure_config) as? Boolean ?: false\n        if (!isParentConfigSet) {\n            throw RuntimeException(\"You should config the exposure on parent($parent) for constraint effect.\")\n        }\n        val holder = parent.getTag(R.id.qmui_exposure_holder) as? Runnable\n        if (holder != null) {\n            parent.removeCallbacks(holder)\n            parent.setTag(R.id.qmui_exposure_holder, null)\n            holder.run()\n        }\n        return if(parent.isInExposure()) EffectResult.pass else EffectResult.unHandled\n    }\n}\n\n\nclass RecyclerExposureEffect(\n    val parent: ViewGroup,\n    val safeDuration: Long = 500,\n    val zombieDuration: Long = 2000\n) : ExposureEffect {\n\n    private val exposureSet = mutableSetOf<Pair<Exposure, Long>>()\n    private val zombieSet = mutableSetOf<Pair<Exposure, Long>>()\n\n    override fun doBeforeExpose(\n        target: View,\n        container: ViewGroup,\n        exposure: Exposure,\n        lastExposure: Exposure?,\n        type: ExposureType\n    ): EffectResult {\n        clearZombie()\n        if(type == ExposureType.dataChange){\n            lastExposure?.also { last ->\n                val exist = exposureSet.find { it.first.same(last) }\n                if(exist == null || exist.second + safeDuration < SystemClock.elapsedRealtime()){\n                    zombieSet.removeAll { it.first.same(last) }\n                    zombieSet.add(last to SystemClock.elapsedRealtime())\n                    if(exist != null){\n                        exposureSet.removeAll { it.first.same(last) }\n                    }\n                }\n            }\n        }\n        if(exposureSet.find { it.first.same(exposure) } != null){\n            zombieSet.removeAll { it.first.same(exposure) }\n            return EffectResult.handled\n        }\n        val zombie = zombieSet.find { it.first.same(exposure) }\n        if(zombie != null){\n            exposureSet.add(exposure to SystemClock.elapsedRealtime())\n            zombieSet.remove(zombie)\n            return EffectResult.handled\n        }\n        zombieSet.removeAll { it.first.same(exposure) }\n        exposureSet.add(exposure to SystemClock.elapsedRealtime())\n        return EffectResult.pass\n    }\n\n    override fun doAfterUnExpose(target: View, container: ViewGroup, data: Exposure) {\n        clearZombie()\n        zombieSet.removeAll { it.first.same(data) }\n        zombieSet.add(data to SystemClock.elapsedRealtime())\n        exposureSet.removeAll { it.first.same(data) }\n    }\n\n    private fun clearZombie(){\n        val iterator = zombieSet.iterator()\n        while (iterator.hasNext()){\n            val next = iterator.next()\n            if(next.second + zombieDuration < SystemClock.elapsedRealtime()){\n                iterator.remove()\n            }\n        }\n    }\n}\n\n\ninternal class ExposureEffectList(\n    val container: ViewGroup,\n    val effectList: List<ExposureEffect>\n)"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/exposure/ExposureEx.kt",
    "content": "package com.qmuiteam.qmui.exposure\n\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.ViewParent\nimport android.view.ViewTreeObserver\nimport android.widget.AbsListView\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.viewpager.widget.ViewPager\nimport com.qmuiteam.qmui.R\nimport com.qmuiteam.qmui.kotlin.debounceRun\nimport com.qmuiteam.qmui.widget.tab.QMUIBasicTabSegment\n\n/**\n *  Exposure 使用：\n *  1. 使用场景：\n *     a. 简单使用：simpleExposure(key=xxx, ...)\n *     b. 复杂使用， view 初始化时 registerExposure(...)， 渲染数据时 bindExposure(Exposure)\n *     c. 和 RecyclerView/ListView 配合，onBindViewHolder 时：simpleExposure(key=xxx, ...)， 或者在 onCreateViewHolder 时 registerExposure(...)，\n *        onBindViewHolder 时 bindExposure(Exposure)\n *     d. 有自定义 View 复用逻辑的容器，同 c, 但 ViewGroup 需要调用 setToRecyclerContainer()\n *     e. 如果子 View 需要在父 View 已曝光的前提下才能认为是曝光， 那么父容器需要调用 setSelfExposedWhenDescendantExposed()\n *\n *  2. Exposure 类\n *     曝光所用的数据类，使用者需要自定义，框架通过 same(Exposure) 判断数据是否变更而觉得是否需要重新曝光， RecyclerView 复用排重也依赖于它\n *     框架在满足曝光时触发 expose() 方法\n *\n *  3. 可配置项：\n *     holdTime -> 需要在可视区域停留超过 holdTime 后才算曝光， 默认 600ms\n *     debounceTimeout -> debounce 处理，防止界面多次 layout / scroll 不停触发曝光检查， 默认 400ms\n *     containerProvider -> 在 containerProvider 提供的 ViewGroup 里可视才算曝光，默认是整个界面的 rootView\n *     exposureChecker -> 曝光检查器，默认是可视面积超过自身总面积的 80% 算可见\n */\n\nfun View.simpleExposure(\n    holdTime: Long = 600,\n    debounceTimeout: Long = 400,\n    containerProvider: ExposureContainerProvider = DefaultExposureContainerProvider,\n    exposureChecker: ExposureChecker = defaultExposureChecker,\n    key: Any?,\n    doExpose: (type: ExposureType) -> Unit\n) {\n    registerExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n    bindExposure(SimpleExposure(key) {\n        doExpose(it)\n    })\n}\n\nfun View.exposure(\n    holdTime: Long = 600,\n    debounceTimeout: Long = 400,\n    containerProvider: ExposureContainerProvider = DefaultExposureContainerProvider,\n    exposureChecker: ExposureChecker = fullExposureChecker,\n    exposure: Exposure\n){\n    registerExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n    bindExposure(exposure)\n}\n\nfun View.registerExposure(\n    holdTime: Long = 600,\n    debounceTimeout: Long = 400,\n    containerProvider: ExposureContainerProvider = DefaultExposureContainerProvider,\n    exposureChecker: ExposureChecker = fullExposureChecker\n) {\n    setTag(R.id.qmui_exposure_config, true)\n    var attachListener = getTag(R.id.qmui_exposure_register) as? View.OnAttachStateChangeListener\n    if(attachListener != null){\n        return\n    }\n    attachListener = object : View.OnAttachStateChangeListener {\n        private val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {\n            checkExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n        }\n\n        private val onScrollListener = ViewTreeObserver.OnScrollChangedListener {\n            checkExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n        }\n\n        private val customTriggerListener = CustomExposureTriggerListener {\n            checkExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n        }\n\n        override fun onViewAttachedToWindow(v: View?) {\n            checkExposure(holdTime, debounceTimeout, containerProvider, exposureChecker)\n            viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)\n            viewTreeObserver.addOnScrollChangedListener(onScrollListener)\n            containerProvider.provide(this@registerExposure)?.let { container ->\n                var exposureCheck = container.getTag(R.id.qmui_exposure_custom_check_trigger) as? CustomExposureTrigger\n                if(exposureCheck == null){\n                    exposureCheck = CustomExposureTrigger().also {\n                        container.setTag(R.id.qmui_exposure_custom_check_trigger, it)\n                    }\n                }\n                exposureCheck.addListener(customTriggerListener)\n            }\n        }\n\n        override fun onViewDetachedFromWindow(v: View?) {\n            viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener)\n            viewTreeObserver.removeOnScrollChangedListener(onScrollListener)\n            containerProvider.provide(this@registerExposure)?.let { container ->\n                (container.getTag(R.id.qmui_exposure_custom_check_trigger) as? CustomExposureTrigger)?.removeListener(customTriggerListener)\n            }\n            clearExposureHolder()\n            clearExposureDebounce()\n            doUnExpose()\n        }\n\n    }\n    setTag(R.id.qmui_exposure_register, attachListener)\n    addOnAttachStateChangeListener(attachListener)\n    if(isAttachedToWindow){\n        attachListener.onViewAttachedToWindow(this)\n    }\n}\n\nfun View.unregisterExposure(){\n    setTag(R.id.qmui_exposure_config, false)\n    val attachListener = getTag(R.id.qmui_exposure_register) as? View.OnAttachStateChangeListener\n    if(attachListener != null){\n        removeOnAttachStateChangeListener(attachListener)\n        attachListener.onViewDetachedFromWindow(this)\n        setTag(R.id.qmui_exposure_register, null)\n    }\n}\n\nfun View.bindExposure(exposure: Exposure) {\n    setTag(R.id.qmui_exposure_data, exposure)\n}\n\nfun View.isInExposure(): Boolean {\n    return getTag(R.id.qmui_exposure_ing) as? Boolean ?: false\n}\n\nfun View.setToRecyclerContainer() {\n    setTag(R.id.qmui_exposure_is_recycler_container, true)\n}\n\nfun ViewGroup.setSelfExposedWhenDescendantExposed(need: Boolean) {\n    if(need){\n        setTag(R.id.qmui_exposure_parent_expose_request, ParentExposedRequestExposureEffect(this))\n    }else{\n        setTag(R.id.qmui_exposure_parent_expose_request, null)\n    }\n\n}\n\nfun ViewGroup.customConfigRecyclerExposureEffect(effect: RecyclerExposureEffect) {\n    setTag(R.id.qmui_exposure_recycler_collection, effect)\n}\n\nfun View.triggerCustomExposureChecker(\n    containerProvider: ExposureContainerProvider = DefaultExposureContainerProvider\n) {\n    if(!isAttachedToWindow){\n        return\n    }\n    (containerProvider.provide(this)?.getTag(R.id.qmui_exposure_custom_check_trigger) as? CustomExposureTrigger)?.trigger()\n}\n\nfun View.defaultCanExpose(): Boolean {\n    if (!isAttachedToWindow) {\n        return false\n    }\n    if (windowVisibility != View.VISIBLE) {\n        return false\n    }\n    if (visibility != View.VISIBLE) {\n        return false\n    }\n    var p: ViewParent? = parent\n    while (p != null && p is ViewGroup) {\n        if (p.visibility != View.VISIBLE) {\n            return false\n        }\n        p = p.parent\n    }\n    return true\n}\n\nfun View.checkExposure(\n    holdTime: Long = 1000,\n    debounceTimeout: Long = 500,\n    containerProvider: ExposureContainerProvider = DefaultExposureContainerProvider,\n    exposureChecker: ExposureChecker = fullExposureChecker,\n) {\n    val holderRunnable = getTag(R.id.qmui_exposure_holder) as? Runnable\n    if (holderRunnable != null) {\n        return\n    }\n    debounceRun(R.id.qmui_exposure_debounce, debounceTimeout) {\n        val container = containerProvider.provide(this) ?: return@debounceRun\n        val isInExposure = isInExposure()\n        if (checkIsExposure(container, exposureChecker)) {\n            if (!isInExposure || checkIsExposureDataChanged()) {\n                val runnable = Runnable {\n                    setTag(R.id.qmui_exposure_holder, null)\n                    if (checkIsExposure(container, exposureChecker)) {\n                        val data = getTag(R.id.qmui_exposure_data) as? Exposure ?: return@Runnable\n                        val last = getTag(R.id.qmui_exposure_last_data) as? Exposure\n                        val type = when {\n                            last == null -> ExposureType.first\n                            !last.same(data) -> ExposureType.dataChange\n                            else -> ExposureType.repeat\n                        }\n                        if (doExpose(container, data, last, type)) {\n                            setTag(R.id.qmui_exposure_ing, true)\n                            setTag(R.id.qmui_exposure_last_data, data)\n                        }\n                    }\n                }.also {\n                    setTag(R.id.qmui_exposure_holder, it)\n                }\n                postDelayed(runnable, holdTime)\n            }\n\n        } else if (isInExposure) {\n            doUnExpose()\n        }\n    }\n}\n\nprivate fun View.checkIsExposureDataChanged(): Boolean {\n    val data = getTag(R.id.qmui_exposure_data) as? Exposure ?: return false\n    val last = getTag(R.id.qmui_exposure_last_data) as? Exposure\n    return last == null || !last.same(data)\n}\n\nprivate fun View.checkIsExposure(\n    container: ViewGroup,\n    exposureChecker: ExposureChecker = fullExposureChecker\n): Boolean {\n    if (!exposureChecker.canExpose(this)) {\n        return false\n    }\n    return exposureChecker.isExposedInContainer(container, this)\n}\n\ninternal fun View.clearExposureHolder() {\n    (getTag(R.id.qmui_exposure_holder) as? Runnable)?.let {\n        removeCallbacks(it)\n        setTag(R.id.qmui_exposure_holder, null)\n    }\n}\n\ninternal fun View.clearExposureDebounce() {\n    (getTag(R.id.qmui_exposure_debounce) as? Runnable)?.let {\n        removeCallbacks(it)\n        setTag(R.id.qmui_exposure_debounce, null)\n    }\n}\n\n\ninternal fun View.doExpose(\n    container: ViewGroup,\n    exposure: Exposure,\n    lastExposure: Exposure?,\n    exposureType: ExposureType\n): Boolean {\n    var p = parent as? ViewGroup\n    val exposureList = mutableListOf<ExposureEffect>()\n    var effectResult = EffectResult.pass\n    while (p != null && p != container) {\n        val parentAlready = p.getTag(R.id.qmui_exposure_parent_expose_request) as? ParentExposedRequestExposureEffect\n        if (parentAlready != null) {\n            exposureList.add(parentAlready)\n            val ret = parentAlready.doBeforeExpose(this, container, exposure, lastExposure, exposureType)\n            if (ret != EffectResult.pass) {\n                effectResult = ret\n                break\n            }\n        }\n        if (parent == p &&\n            (p is RecyclerView ||\n                    p is AbsListView ||\n                    p is QMUIBasicTabSegment ||\n                    p is ViewPager ||\n                    p.getTag(R.id.qmui_exposure_is_recycler_container) == true)\n        ) {\n            var recyclerEffect = p.getTag(R.id.qmui_exposure_recycler_collection) as? RecyclerExposureEffect\n            if (recyclerEffect == null) {\n                recyclerEffect = RecyclerExposureEffect(p)\n                p.setTag(R.id.qmui_exposure_recycler_collection, recyclerEffect)\n            }\n            exposureList.add(recyclerEffect)\n            val ret = recyclerEffect.doBeforeExpose(this, container, exposure, lastExposure, exposureType)\n            if (ret != EffectResult.pass) {\n                effectResult = ret\n                break\n            }\n        }\n\n        val customEffect = p.getTag(R.id.qmui_exposure_custom_effect) as? ExposureEffect\n        if (customEffect != null) {\n            exposureList.add(customEffect)\n            val ret = customEffect.doBeforeExpose(this, container, exposure, lastExposure, exposureType)\n            if (ret != EffectResult.pass) {\n                effectResult = ret\n                break\n            }\n        }\n\n        p = p.parent as? ViewGroup\n    }\n    setTag(R.id.qmui_exposure_effect_list, ExposureEffectList(container, exposureList))\n    if (effectResult == EffectResult.pass) {\n        exposure.expose(this, exposureType)\n        effectResult = EffectResult.handled\n    }\n    return effectResult == EffectResult.handled\n}\n\ninternal fun View.doUnExpose() {\n    if (isInExposure()) {\n        setTag(R.id.qmui_exposure_ing, false)\n        val exposure = getTag(R.id.qmui_exposure_data) as? Exposure ?: return\n        (getTag(R.id.qmui_exposure_effect_list) as? ExposureEffectList)?.let {\n            it.effectList.forEach { effect ->\n                effect.doAfterUnExpose(this, it.container, exposure)\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/kotlin/DimenKt.kt",
    "content": "package com.qmuiteam.qmui.kotlin\n\nimport android.content.Context\nimport android.view.View\nimport androidx.annotation.DimenRes\nimport androidx.fragment.app.Fragment\n\nfun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()\nfun Context.dip(value: Float): Int = (value * resources.displayMetrics.density).toInt()\nfun Context.sp(value: Int): Int = (value * resources.displayMetrics.scaledDensity).toInt()\nfun Context.sp(value: Float): Int = (value * resources.displayMetrics.scaledDensity).toInt()\nfun Context.px2dp(px: Int): Float = px.toFloat() / resources.displayMetrics.density\nfun Context.px2sp(px: Int): Float = px.toFloat() / resources.displayMetrics.scaledDensity\nfun Context.dimen(@DimenRes resource: Int): Int = resources.getDimensionPixelSize(resource)\n\nfun View.dip(value: Int): Int = context.dip(value)\nfun View.dip(value: Float): Int = context.dip(value)\nfun View.sp(value: Int): Int = context.sp(value)\nfun View.sp(value: Float): Int = context.sp(value)\nfun View.px2dp(px: Int): Float = context.px2dp(px)\nfun View.px2sp(px: Int): Float = context.px2sp(px)\nfun View.dimen(@DimenRes resource: Int): Int = context.dimen(resource)\n\n// must be called after attached.\nfun Fragment.dip(value: Int): Int = context!!.dip(value)\nfun Fragment.dip(value: Float): Int = context!!.dip(value)\nfun Fragment.sp(value: Int): Int = context!!.sp(value)\nfun Fragment.sp(value: Float): Int = context!!.sp(value)\nfun Fragment.px2dp(px: Int): Float = context!!.px2dp(px)\nfun Fragment.px2sp(px: Int): Float = context!!.px2sp(px)\nfun Fragment.dimen(@DimenRes resource: Int): Int = context!!.dimen(resource)"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/kotlin/LayoutParamKt.kt",
    "content": "package com.qmuiteam.qmui.kotlin\n\nimport android.view.ViewGroup\nimport androidx.constraintlayout.widget.ConstraintLayout\n\nval matchParent: Int = ViewGroup.LayoutParams.MATCH_PARENT\nval wrapContent: Int = ViewGroup.LayoutParams.WRAP_CONTENT\nval matchConstraint: Int = ConstraintLayout.LayoutParams.MATCH_CONSTRAINT\nval constraintParentId = ConstraintLayout.LayoutParams.PARENT_ID\n\nfun ConstraintLayout.LayoutParams.alignParent4(){\n    leftToLeft = constraintParentId\n    rightToRight = constraintParentId\n    topToTop = constraintParentId\n    bottomToBottom = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentHor(){\n    leftToLeft = constraintParentId\n    rightToRight = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentVer(){\n    topToTop = constraintParentId\n    bottomToBottom = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentLeftTop(){\n    topToTop = constraintParentId\n    leftToLeft = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentLeftBottom(){\n    bottomToBottom = constraintParentId\n    leftToLeft = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentRightTop(){\n    topToTop = constraintParentId\n    rightToRight = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignParentRightBottom(){\n    bottomToBottom = constraintParentId\n    rightToRight = constraintParentId\n}\n\nfun ConstraintLayout.LayoutParams.alignView4(id: Int){\n    leftToLeft = id\n    rightToRight = id\n    topToTop = id\n    bottomToBottom = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewHor(id: Int){\n    leftToLeft = id\n    rightToRight = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewVer(id: Int){\n    topToTop = id\n    bottomToBottom = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewLeftTop(id: Int){\n    topToTop = id\n    leftToLeft = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewLeftBottom(id: Int){\n    bottomToBottom = id\n    leftToLeft = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewRightTop(id: Int){\n    topToTop = id\n    rightToRight = id\n}\n\nfun ConstraintLayout.LayoutParams.alignViewRightBottom(id: Int){\n    bottomToBottom = id\n    rightToRight = id\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/kotlin/ViewKt.kt",
    "content": "package com.qmuiteam.qmui.kotlin\n\nimport android.os.SystemClock\nimport android.view.View\nimport com.qmuiteam.qmui.R\nimport com.qmuiteam.qmui.skin.QMUISkinHelper\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder\n\nfun View.throttleRun(\n    id: Int,\n    timeout: Long,\n    block: () -> Unit\n){\n    val exit = getTag(id) as? Runnable\n    if(exit != null){\n        return\n    }\n    val nextThrottle = Runnable {\n        setTag(id, null)\n        block()\n    }.also {\n        setTag(id, it)\n    }\n    postDelayed(nextThrottle, timeout)\n}\n\nfun View.debounceRun(\n    id: Int,\n    timeout: Long,\n    block: () -> Unit\n){\n    val exit = getTag(id) as? Runnable\n    if(exit != null){\n        removeCallbacks(exit)\n        postDelayed(exit, timeout)\n        return\n    }\n    val nextThrottle = Runnable {\n        setTag(id, null)\n        block()\n    }.also {\n        setTag(id, it)\n    }\n    postDelayed(nextThrottle, timeout)\n}\n\nfun throttleClick(wait: Long = 200, block: ((View) -> Unit)): View.OnClickListener {\n\n    return View.OnClickListener { v ->\n        val current = SystemClock.uptimeMillis()\n        val lastClickTime = (v.getTag(R.id.qmui_click_timestamp) as? Long) ?: 0\n        if (current - lastClickTime > wait) {\n            v.setTag(R.id.qmui_click_timestamp, current)\n            block(v)\n        }\n    }\n}\n\nfun debounceClick(wait: Long = 200, block: ((View) -> Unit)): View.OnClickListener {\n    return View.OnClickListener { v ->\n        var action = (v.getTag(R.id.qmui_click_debounce_action) as? DebounceAction)\n        if(action == null){\n            action = DebounceAction(v, block)\n            v.setTag(R.id.qmui_click_debounce_action, action)\n        }else{\n            action.block = block\n        }\n        v.removeCallbacks(action)\n        v.postDelayed(action, wait)\n    }\n}\n\nclass DebounceAction(val view: View,  var block: ((View) -> Unit)): Runnable {\n    override fun run() {\n        if(view.isAttachedToWindow){\n            block(view)\n        }\n    }\n}\n\nfun View.onClick(wait: Long = 200, block: ((View) -> Unit)) {\n    setOnClickListener(throttleClick(wait, block))\n}\n\nfun View.onDebounceClick(wait: Long = 200, block: ((View) -> Unit)) {\n    setOnClickListener(debounceClick(wait, block))\n}\n\nfun View.skin(increment: Boolean = false, block:(QMUISkinValueBuilder.() -> Unit)){\n    val builder = QMUISkinValueBuilder.acquire()\n    if(increment){\n        val oldSkinValue = getTag(R.id.qmui_skin_value)\n        if(oldSkinValue is String){\n            builder.convertFrom(oldSkinValue)\n        }\n    }\n    builder.block()\n    QMUISkinHelper.setSkinValue(this, builder)\n    builder.release()\n}\n\nfun View.clearSkin(){\n    QMUISkinHelper.setSkinValue(this, \"\")\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/IQMUILayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.view.View;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.IntDef;\n\n/**\n * Created by cgspine on 2018/3/23.\n */\n\npublic interface IQMUILayout {\n    int HIDE_RADIUS_SIDE_NONE = 0;\n    int HIDE_RADIUS_SIDE_TOP = 1;\n    int HIDE_RADIUS_SIDE_RIGHT = 2;\n    int HIDE_RADIUS_SIDE_BOTTOM = 3;\n    int HIDE_RADIUS_SIDE_LEFT = 4;\n\n    @IntDef(value = {\n            HIDE_RADIUS_SIDE_NONE,\n            HIDE_RADIUS_SIDE_TOP,\n            HIDE_RADIUS_SIDE_RIGHT,\n            HIDE_RADIUS_SIDE_BOTTOM,\n            HIDE_RADIUS_SIDE_LEFT})\n    @Retention(RetentionPolicy.SOURCE)\n    @interface HideRadiusSide {\n    }\n\n    /**\n     * limit the width of a layout\n     *\n     * @param widthLimit\n     * @return\n     */\n    boolean setWidthLimit(int widthLimit);\n\n    /**\n     * limit the height of a layout\n     *\n     * @param heightLimit\n     * @return\n     */\n    boolean setHeightLimit(int heightLimit);\n\n    /**\n     * use the shadow elevation from the theme\n     */\n    void setUseThemeGeneralShadowElevation();\n\n    /**\n     * determine if the outline contain the padding area, usually false\n     *\n     * @param outlineExcludePadding\n     */\n    void setOutlineExcludePadding(boolean outlineExcludePadding);\n\n    /**\n     * See {@link android.view.View#setElevation(float)}\n     *\n     * @param elevation\n     */\n    void setShadowElevation(int elevation);\n\n    /**\n     * See {@link View#getElevation()}\n     *\n     * @return\n     */\n    int getShadowElevation();\n\n    /**\n     * set the outline alpha, which will change the shadow\n     *\n     * @param shadowAlpha\n     */\n    void setShadowAlpha(float shadowAlpha);\n\n    /**\n     * get the outline alpha we set\n     *\n     * @return\n     */\n    float getShadowAlpha();\n\n    /**\n     * @param shadowColor opaque color\n     * @return\n     */\n    void setShadowColor(int shadowColor);\n\n    /**\n     * @return opaque color\n     */\n    int getShadowColor();\n\n    /**\n     * set the layout radius\n     *\n     * @param radius\n     */\n    void setRadius(int radius);\n\n    /**\n     * set the layout radius with one or none side been hidden\n     *\n     * @param radius\n     * @param hideRadiusSide\n     */\n    void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide);\n\n    /**\n     * get the layout radius\n     *\n     * @return\n     */\n    int getRadius();\n\n    /**\n     * inset the outline if needed\n     *\n     * @param left\n     * @param top\n     * @param right\n     * @param bottom\n     */\n    void setOutlineInset(int left, int top, int right, int bottom);\n\n    /**\n     * the shadow elevation only work after L, so we provide a downgrading compatible solutions for android 4.x\n     * usually we use border, but the border may be redundant for android L+. so will not show border default,\n     * if your designer like the border exists with shadow, you can call setShowBorderOnlyBeforeL(false)\n     *\n     * @param showBorderOnlyBeforeL\n     */\n    void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL);\n\n    /**\n     * in some case, we maybe hope the layout only have radius in one side.\n     * but there is no convenient way to write the code like canvas.drawPath,\n     * so we take another way that hide one radius side\n     *\n     * @param hideRadiusSide\n     */\n    void setHideRadiusSide(@HideRadiusSide int hideRadiusSide);\n\n    /**\n     * get the side that we have hidden the radius\n     *\n     * @return\n     */\n    int getHideRadiusSide();\n\n    /**\n     * this method will determine the radius and shadow.\n     *\n     * @param radius\n     * @param shadowElevation\n     * @param shadowAlpha\n     */\n    void setRadiusAndShadow(int radius, int shadowElevation, float shadowAlpha);\n\n    /**\n     * this method will determine the radius and shadow with one or none side be hidden\n     *\n     * @param radius\n     * @param hideRadiusSide\n     * @param shadowElevation\n     * @param shadowAlpha\n     */\n    void setRadiusAndShadow(int radius, @HideRadiusSide int hideRadiusSide, int shadowElevation, float shadowAlpha);\n\n\n    /**\n     * this method will determine the radius and shadow (support shadowColor if after android 9)with one or none side be hidden\n     *\n     * @param radius\n     * @param hideRadiusSide\n     * @param shadowElevation\n     * @param shadowColor\n     * @param shadowAlpha\n     */\n    void setRadiusAndShadow(int radius, @HideRadiusSide int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha);\n\n    /**\n     * border color, if you don not set it, the layout will not draw the border\n     *\n     * @param borderColor\n     */\n    void setBorderColor(@ColorInt int borderColor);\n\n    /**\n     * border width, default is 1px, usually no need to set\n     *\n     * @param borderWidth\n     */\n    void setBorderWidth(int borderWidth);\n\n    /**\n     * config the top divider\n     *\n     * @param topInsetLeft\n     * @param topInsetRight\n     * @param topDividerHeight\n     * @param topDividerColor\n     */\n    void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor);\n\n    /**\n     * config the bottom divider\n     *\n     * @param bottomInsetLeft\n     * @param bottomInsetRight\n     * @param bottomDividerHeight\n     * @param bottomDividerColor\n     */\n    void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor);\n\n    /**\n     * config the left divider\n     *\n     * @param leftInsetTop\n     * @param leftInsetBottom\n     * @param leftDividerWidth\n     * @param leftDividerColor\n     */\n    void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor);\n\n    /**\n     * config the right divider\n     *\n     * @param rightInsetTop\n     * @param rightInsetBottom\n     * @param rightDividerWidth\n     * @param rightDividerColor\n     */\n    void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor);\n\n    /**\n     * show top divider, and hide others\n     *\n     * @param topInsetLeft\n     * @param topInsetRight\n     * @param topDividerHeight\n     * @param topDividerColor\n     */\n    void onlyShowTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor);\n\n    /**\n     * show bottom divider, and hide others\n     *\n     * @param bottomInsetLeft\n     * @param bottomInsetRight\n     * @param bottomDividerHeight\n     * @param bottomDividerColor\n     */\n    void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor);\n\n    /**\n     * show left divider, and hide others\n     *\n     * @param leftInsetTop\n     * @param leftInsetBottom\n     * @param leftDividerWidth\n     * @param leftDividerColor\n     */\n    void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor);\n\n    /**\n     * show right divider, and hide others\n     *\n     * @param rightInsetTop\n     * @param rightInsetBottom\n     * @param rightDividerWidth\n     * @param rightDividerColor\n     */\n    void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor);\n\n    /**\n     * after config the border, sometimes we need change the alpha of divider with animation,\n     * so we provide a method to individually change the alpha\n     *\n     * @param dividerAlpha [0, 255]\n     */\n    void setTopDividerAlpha(int dividerAlpha);\n\n    /**\n     * @param dividerAlpha [0, 255]\n     */\n    void setBottomDividerAlpha(int dividerAlpha);\n\n    /**\n     * @param dividerAlpha [0, 255]\n     */\n    void setLeftDividerAlpha(int dividerAlpha);\n\n    /**\n     * @param dividerAlpha [0, 255]\n     */\n    void setRightDividerAlpha(int dividerAlpha);\n\n    /**\n     * only available before android L\n     *\n     * @param color\n     */\n    void setOuterNormalColor(int color);\n\n    /**\n     * update left separator color\n     *\n     * @param color\n     */\n    void updateLeftSeparatorColor(int color);\n\n    /**\n     * update right separator color\n     *\n     * @param color\n     */\n    void updateRightSeparatorColor(int color);\n\n    /**\n     * update top separator color\n     *\n     * @param color\n     */\n    void updateTopSeparatorColor(int color);\n\n    /**\n     * update bottom separator color\n     *\n     * @param color\n     */\n    void updateBottomSeparatorColor(int color);\n\n    boolean hasTopSeparator();\n\n    boolean hasRightSeparator();\n\n    boolean hasLeftSeparator();\n\n    boolean hasBottomSeparator();\n\n    boolean hasBorder();\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUIButton.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport androidx.annotation.ColorInt;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.alpha.QMUIAlphaButton;\n\n/**\n * Created by cgspine on 2018/3/1.\n */\n\npublic class QMUIButton extends QMUIAlphaButton implements IQMUILayout {\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUIButton(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIButton(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIButton(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenDisable(false);\n        setChangeAlphaWhenPress(false);\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n        invalidate();\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUIConstraintLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.ColorInt;\n\nimport com.qmuiteam.qmui.alpha.QMUIAlphaConstraintLayout;\n\n/**\n * @author cginechen\n * @date 2017-03-10\n */\n\npublic class QMUIConstraintLayout extends QMUIAlphaConstraintLayout implements IQMUILayout {\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUIConstraintLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIConstraintLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenPress(false);\n        setChangeAlphaWhenDisable(false);\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void dispatchDraw(Canvas canvas) {\n        try {\n            super.dispatchDraw(canvas);\n            mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n            mLayoutHelper.dispatchRoundBorderDraw(canvas);\n        }catch (Throwable ignore){\n            // unreasonable crash\n        }\n\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUIFrameLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport androidx.annotation.ColorInt;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.alpha.QMUIAlphaFrameLayout;\n\n/**\n * @author cginechen\n * @date 2017-03-10\n */\n\npublic class QMUIFrameLayout extends QMUIAlphaFrameLayout implements IQMUILayout {\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUIFrameLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenDisable(false);\n        setChangeAlphaWhenPress(false);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUILayoutHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Outline;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.RectF;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewOutlineProvider;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.lang.ref.WeakReference;\n\nimport androidx.annotation.ColorInt;\nimport androidx.core.content.ContextCompat;\n\n/**\n * @author cginechen\n * @date 2017-03-10\n */\n\npublic class    QMUILayoutHelper implements IQMUILayout {\n    public static final int RADIUS_OF_HALF_VIEW_HEIGHT = -1;\n    public static final int RADIUS_OF_HALF_VIEW_WIDTH = -2;\n    private Context mContext;\n    // size\n    private int mWidthLimit = 0;\n    private int mHeightLimit = 0;\n    private int mWidthMini = 0;\n    private int mHeightMini = 0;\n\n\n    // divider\n    private int mTopDividerHeight = 0;\n    private int mTopDividerInsetLeft = 0;\n    private int mTopDividerInsetRight = 0;\n    private int mTopDividerColor;\n    private int mTopDividerAlpha = 255;\n\n    private int mBottomDividerHeight = 0;\n    private int mBottomDividerInsetLeft = 0;\n    private int mBottomDividerInsetRight = 0;\n    private int mBottomDividerColor;\n    private int mBottomDividerAlpha = 255;\n\n    private int mLeftDividerWidth = 0;\n    private int mLeftDividerInsetTop = 0;\n    private int mLeftDividerInsetBottom = 0;\n    private int mLeftDividerColor;\n    private int mLeftDividerAlpha = 255;\n\n    private int mRightDividerWidth = 0;\n    private int mRightDividerInsetTop = 0;\n    private int mRightDividerInsetBottom = 0;\n    private int mRightDividerColor;\n    private int mRightDividerAlpha = 255;\n    private Paint mDividerPaint;\n\n    // round\n    private Paint mClipPaint;\n    private PorterDuffXfermode mMode;\n    private int mRadius;\n    private @IQMUILayout.HideRadiusSide int mHideRadiusSide = HIDE_RADIUS_SIDE_NONE;\n    private float[] mRadiusArray;\n    private boolean mShouldUseRadiusArray;\n    private RectF mBorderRect;\n    private int mBorderColor = 0;\n    private int mBorderWidth = 1;\n    private int mOuterNormalColor = 0;\n    private WeakReference<View> mOwner;\n    private boolean mIsOutlineExcludePadding = false;\n    private Path mPath = new Path();\n\n    // shadow\n    private boolean mIsShowBorderOnlyBeforeL = true;\n    private int mShadowElevation = 0;\n    private float mShadowAlpha;\n    private int mShadowColor = Color.BLACK;\n\n    // outline inset\n    private int mOutlineInsetLeft = 0;\n    private int mOutlineInsetRight = 0;\n    private int mOutlineInsetTop = 0;\n    private int mOutlineInsetBottom = 0;\n\n    public QMUILayoutHelper(Context context, AttributeSet attrs, int defAttr, View owner) {\n        this(context, attrs, defAttr, 0, owner);\n    }\n\n    public QMUILayoutHelper(Context context, AttributeSet attrs, int defAttr, int defStyleRes, View owner) {\n        mContext = context;\n        mOwner = new WeakReference<>(owner);\n        mBottomDividerColor = mTopDividerColor =\n                ContextCompat.getColor(context, R.color.qmui_config_color_separator);\n        mMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);\n        mClipPaint = new Paint();\n        mClipPaint.setAntiAlias(true);\n        mShadowAlpha = QMUIResHelper.getAttrFloatValue(context, R.attr.qmui_general_shadow_alpha);\n        mBorderRect = new RectF();\n\n        int radius = 0, shadow = 0;\n        boolean useThemeGeneralShadowElevation = false;\n        if (null != attrs || defAttr != 0 || defStyleRes != 0) {\n            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QMUILayout, defAttr, defStyleRes);\n            int count = ta.getIndexCount();\n            for (int i = 0; i < count; ++i) {\n                int index = ta.getIndex(i);\n                if (index == R.styleable.QMUILayout_android_maxWidth) {\n                    mWidthLimit = ta.getDimensionPixelSize(index, mWidthLimit);\n                } else if (index == R.styleable.QMUILayout_android_maxHeight) {\n                    mHeightLimit = ta.getDimensionPixelSize(index, mHeightLimit);\n                } else if (index == R.styleable.QMUILayout_android_minWidth) {\n                    mWidthMini = ta.getDimensionPixelSize(index, mWidthMini);\n                } else if (index == R.styleable.QMUILayout_android_minHeight) {\n                    mHeightMini = ta.getDimensionPixelSize(index, mHeightMini);\n                } else if (index == R.styleable.QMUILayout_qmui_topDividerColor) {\n                    mTopDividerColor = ta.getColor(index, mTopDividerColor);\n                } else if (index == R.styleable.QMUILayout_qmui_topDividerHeight) {\n                    mTopDividerHeight = ta.getDimensionPixelSize(index, mTopDividerHeight);\n                } else if (index == R.styleable.QMUILayout_qmui_topDividerInsetLeft) {\n                    mTopDividerInsetLeft = ta.getDimensionPixelSize(index, mTopDividerInsetLeft);\n                } else if (index == R.styleable.QMUILayout_qmui_topDividerInsetRight) {\n                    mTopDividerInsetRight = ta.getDimensionPixelSize(index, mTopDividerInsetRight);\n                } else if (index == R.styleable.QMUILayout_qmui_bottomDividerColor) {\n                    mBottomDividerColor = ta.getColor(index, mBottomDividerColor);\n                } else if (index == R.styleable.QMUILayout_qmui_bottomDividerHeight) {\n                    mBottomDividerHeight = ta.getDimensionPixelSize(index, mBottomDividerHeight);\n                } else if (index == R.styleable.QMUILayout_qmui_bottomDividerInsetLeft) {\n                    mBottomDividerInsetLeft = ta.getDimensionPixelSize(index, mBottomDividerInsetLeft);\n                } else if (index == R.styleable.QMUILayout_qmui_bottomDividerInsetRight) {\n                    mBottomDividerInsetRight = ta.getDimensionPixelSize(index, mBottomDividerInsetRight);\n                } else if (index == R.styleable.QMUILayout_qmui_leftDividerColor) {\n                    mLeftDividerColor = ta.getColor(index, mLeftDividerColor);\n                } else if (index == R.styleable.QMUILayout_qmui_leftDividerWidth) {\n                    mLeftDividerWidth = ta.getDimensionPixelSize(index, mLeftDividerWidth);\n                } else if (index == R.styleable.QMUILayout_qmui_leftDividerInsetTop) {\n                    mLeftDividerInsetTop = ta.getDimensionPixelSize(index, mLeftDividerInsetTop);\n                } else if (index == R.styleable.QMUILayout_qmui_leftDividerInsetBottom) {\n                    mLeftDividerInsetBottom = ta.getDimensionPixelSize(index, mLeftDividerInsetBottom);\n                } else if (index == R.styleable.QMUILayout_qmui_rightDividerColor) {\n                    mRightDividerColor = ta.getColor(index, mRightDividerColor);\n                } else if (index == R.styleable.QMUILayout_qmui_rightDividerWidth) {\n                    mRightDividerWidth = ta.getDimensionPixelSize(index, mRightDividerWidth);\n                } else if (index == R.styleable.QMUILayout_qmui_rightDividerInsetTop) {\n                    mRightDividerInsetTop = ta.getDimensionPixelSize(index, mRightDividerInsetTop);\n                } else if (index == R.styleable.QMUILayout_qmui_rightDividerInsetBottom) {\n                    mRightDividerInsetBottom = ta.getDimensionPixelSize(index, mRightDividerInsetBottom);\n                } else if (index == R.styleable.QMUILayout_qmui_borderColor) {\n                    mBorderColor = ta.getColor(index, mBorderColor);\n                } else if (index == R.styleable.QMUILayout_qmui_borderWidth) {\n                    mBorderWidth = ta.getDimensionPixelSize(index, mBorderWidth);\n                } else if (index == R.styleable.QMUILayout_qmui_radius) {\n                    radius = ta.getDimensionPixelSize(index, 0);\n                } else if (index == R.styleable.QMUILayout_qmui_outerNormalColor) {\n                    mOuterNormalColor = ta.getColor(index, mOuterNormalColor);\n                } else if (index == R.styleable.QMUILayout_qmui_hideRadiusSide) {\n                    mHideRadiusSide = ta.getInt(index, mHideRadiusSide);\n                } else if (index == R.styleable.QMUILayout_qmui_showBorderOnlyBeforeL) {\n                    mIsShowBorderOnlyBeforeL = ta.getBoolean(index, mIsShowBorderOnlyBeforeL);\n                } else if (index == R.styleable.QMUILayout_qmui_shadowElevation) {\n                    shadow = ta.getDimensionPixelSize(index, shadow);\n                } else if (index == R.styleable.QMUILayout_qmui_shadowAlpha) {\n                    mShadowAlpha = ta.getFloat(index, mShadowAlpha);\n                } else if (index == R.styleable.QMUILayout_qmui_useThemeGeneralShadowElevation) {\n                    useThemeGeneralShadowElevation = ta.getBoolean(index, false);\n                } else if (index == R.styleable.QMUILayout_qmui_outlineInsetLeft) {\n                    mOutlineInsetLeft = ta.getDimensionPixelSize(index, 0);\n                } else if (index == R.styleable.QMUILayout_qmui_outlineInsetRight) {\n                    mOutlineInsetRight = ta.getDimensionPixelSize(index, 0);\n                } else if (index == R.styleable.QMUILayout_qmui_outlineInsetTop) {\n                    mOutlineInsetTop = ta.getDimensionPixelSize(index, 0);\n                } else if (index == R.styleable.QMUILayout_qmui_outlineInsetBottom) {\n                    mOutlineInsetBottom = ta.getDimensionPixelSize(index, 0);\n                } else if (index == R.styleable.QMUILayout_qmui_outlineExcludePadding) {\n                    mIsOutlineExcludePadding = ta.getBoolean(index, false);\n                }\n            }\n            ta.recycle();\n        }\n        if (shadow == 0 && useThemeGeneralShadowElevation) {\n            shadow = QMUIResHelper.getAttrDimen(context, R.attr.qmui_general_shadow_elevation);\n\n        }\n        setRadiusAndShadow(radius, mHideRadiusSide, shadow, mShadowAlpha);\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mShadowElevation = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_general_shadow_elevation);\n        setRadiusAndShadow(mRadius, mHideRadiusSide, mShadowElevation, mShadowAlpha);\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        if (useFeature()) {\n            View owner = mOwner.get();\n            if (owner == null) {\n                return;\n            }\n            mIsOutlineExcludePadding = outlineExcludePadding;\n            owner.invalidateOutline();\n        }\n\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mWidthLimit != widthLimit) {\n            mWidthLimit = widthLimit;\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mHeightLimit != heightLimit) {\n            mHeightLimit = heightLimit;\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        if (mLeftDividerColor != color) {\n            mLeftDividerColor = color;\n            invalidate();\n        }\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        if (mBottomDividerColor != color) {\n            mBottomDividerColor = color;\n            invalidate();\n        }\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        if (mTopDividerColor != color) {\n            mTopDividerColor = color;\n            invalidate();\n        }\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        if (mRightDividerColor != color) {\n            mRightDividerColor = color;\n            invalidate();\n        }\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mShadowElevation;\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mShadowAlpha;\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mShadowColor;\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        if (useFeature()) {\n            View owner = mOwner.get();\n            if (owner == null) {\n                return;\n            }\n            mOutlineInsetLeft = left;\n            mOutlineInsetRight = right;\n            mOutlineInsetTop = top;\n            mOutlineInsetBottom = bottom;\n            owner.invalidateOutline();\n        }\n    }\n\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mIsShowBorderOnlyBeforeL = showBorderOnlyBeforeL;\n        invalidate();\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        if (mShadowElevation == elevation) {\n            return;\n        }\n        mShadowElevation = elevation;\n        invalidateOutline();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        if (mShadowAlpha == shadowAlpha) {\n            return;\n        }\n        mShadowAlpha = shadowAlpha;\n        invalidateOutline();\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        if (mShadowColor == shadowColor) {\n            return;\n        }\n        mShadowColor = shadowColor;\n        setShadowColorInner(mShadowColor);\n    }\n\n    private void setShadowColorInner(int shadowColor) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            View owner = mOwner.get();\n            if (owner == null) {\n                return;\n            }\n            owner.setOutlineAmbientShadowColor(shadowColor);\n            owner.setOutlineSpotShadowColor(shadowColor);\n        }\n    }\n\n    private void invalidateOutline() {\n        if (useFeature()) {\n            View owner = mOwner.get();\n            if (owner == null) {\n                return;\n            }\n            if (mShadowElevation == 0) {\n                owner.setElevation(0);\n            } else {\n                owner.setElevation(mShadowElevation);\n            }\n            owner.invalidateOutline();\n        }\n    }\n\n    private void invalidate() {\n        View owner = mOwner.get();\n        if (owner == null) {\n            return;\n        }\n        owner.invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(@HideRadiusSide int hideRadiusSide) {\n        if (mHideRadiusSide == hideRadiusSide) {\n            return;\n        }\n        setRadiusAndShadow(mRadius, hideRadiusSide, mShadowElevation, mShadowAlpha);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mHideRadiusSide;\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        if (mRadius != radius) {\n            setRadiusAndShadow(radius, mShadowElevation, mShadowAlpha);\n        }\n    }\n\n    @Override\n    public void setRadius(int radius, @IQMUILayout.HideRadiusSide int hideRadiusSide) {\n        if (mRadius == radius && hideRadiusSide == mHideRadiusSide) {\n            return;\n        }\n        setRadiusAndShadow(radius, hideRadiusSide, mShadowElevation, mShadowAlpha);\n    }\n\n    @Override\n    public int getRadius() {\n        return mRadius;\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, float shadowAlpha) {\n        setRadiusAndShadow(radius, mHideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @IQMUILayout.HideRadiusSide int hideRadiusSide, int shadowElevation, float shadowAlpha) {\n        setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, mShadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        final View owner = mOwner.get();\n        if (owner == null) {\n            return;\n        }\n\n        mRadius = radius;\n        mHideRadiusSide = hideRadiusSide;\n\n        mShouldUseRadiusArray = isRadiusWithSideHidden();\n        mShadowElevation = shadowElevation;\n        mShadowAlpha = shadowAlpha;\n        mShadowColor = shadowColor;\n        if (useFeature()) {\n            if (mShadowElevation == 0 || mShouldUseRadiusArray) {\n                owner.setElevation(0);\n            } else {\n                owner.setElevation(mShadowElevation);\n            }\n\n            setShadowColorInner(mShadowColor);\n\n            owner.setOutlineProvider(new ViewOutlineProvider() {\n                @Override\n                @TargetApi(21)\n                public void getOutline(View view, Outline outline) {\n                    int w = view.getWidth(), h = view.getHeight();\n                    if (w == 0 || h == 0) {\n                        return;\n                    }\n                    float radius = getRealRadius();\n                    int min = Math.min(w, h);\n                    if (radius * 2 > min) {\n                        // 解决 OnePlus 3T 8.0 上显示变形\n                        radius = min / 2F;\n                    }\n                    if (mShouldUseRadiusArray) {\n                        int left = 0, top = 0, right = w, bottom = h;\n                        if (mHideRadiusSide == HIDE_RADIUS_SIDE_LEFT) {\n                            left -= radius;\n                        } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_TOP) {\n                            top -= radius;\n                        } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_RIGHT) {\n                            right += radius;\n                        } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_BOTTOM) {\n                            bottom += radius;\n                        }\n                        outline.setRoundRect(left, top,\n                                right, bottom, radius);\n                        return;\n                    }\n\n                    int top = mOutlineInsetTop, bottom = Math.max(top + 1, h - mOutlineInsetBottom),\n                            left = mOutlineInsetLeft, right = w - mOutlineInsetRight;\n                    if (mIsOutlineExcludePadding) {\n                        left += view.getPaddingLeft();\n                        top += view.getPaddingTop();\n                        right = Math.max(left + 1, right - view.getPaddingRight());\n                        bottom = Math.max(top + 1, bottom - view.getPaddingBottom());\n                    }\n\n                    float shadowAlpha = mShadowAlpha;\n                    if (mShadowElevation == 0) {\n                        // outline.setAlpha will work even if shadowElevation == 0\n                        shadowAlpha = 1f;\n                    }\n\n                    outline.setAlpha(shadowAlpha);\n\n                    if (radius <= 0) {\n                        outline.setRect(left, top,\n                                right, bottom);\n                    } else {\n                        outline.setRoundRect(left, top,\n                                right, bottom, radius);\n                    }\n                }\n            });\n            owner.setClipToOutline(mRadius == RADIUS_OF_HALF_VIEW_WIDTH || mRadius == RADIUS_OF_HALF_VIEW_HEIGHT || mRadius > 0);\n\n        }\n        owner.invalidate();\n    }\n\n    /**\n     * 有radius, 但是有一边不显示radius。\n     *\n     * @return\n     */\n    public boolean isRadiusWithSideHidden() {\n        return (mRadius == RADIUS_OF_HALF_VIEW_HEIGHT ||\n                mRadius == RADIUS_OF_HALF_VIEW_WIDTH ||\n                mRadius > 0) && mHideRadiusSide != HIDE_RADIUS_SIDE_NONE;\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mTopDividerInsetLeft = topInsetLeft;\n        mTopDividerInsetRight = topInsetRight;\n        mTopDividerHeight = topDividerHeight;\n        mTopDividerColor = topDividerColor;\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mBottomDividerInsetLeft = bottomInsetLeft;\n        mBottomDividerInsetRight = bottomInsetRight;\n        mBottomDividerColor = bottomDividerColor;\n        mBottomDividerHeight = bottomDividerHeight;\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLeftDividerInsetTop = leftInsetTop;\n        mLeftDividerInsetBottom = leftInsetBottom;\n        mLeftDividerWidth = leftDividerWidth;\n        mLeftDividerColor = leftDividerColor;\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mRightDividerInsetTop = rightInsetTop;\n        mRightDividerInsetBottom = rightInsetBottom;\n        mRightDividerWidth = rightDividerWidth;\n        mRightDividerColor = rightDividerColor;\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        mLeftDividerWidth = 0;\n        mRightDividerWidth = 0;\n        mBottomDividerHeight = 0;\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        mLeftDividerWidth = 0;\n        mRightDividerWidth = 0;\n        mTopDividerHeight = 0;\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        mRightDividerWidth = 0;\n        mTopDividerHeight = 0;\n        mBottomDividerHeight = 0;\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        mLeftDividerWidth = 0;\n        mTopDividerHeight = 0;\n        mBottomDividerHeight = 0;\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mTopDividerAlpha = dividerAlpha;\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mBottomDividerAlpha = dividerAlpha;\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLeftDividerAlpha = dividerAlpha;\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mRightDividerAlpha = dividerAlpha;\n    }\n\n\n    public int handleMiniWidth(int widthMeasureSpec, int measuredWidth) {\n        if (View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY\n                && measuredWidth < mWidthMini) {\n            return View.MeasureSpec.makeMeasureSpec(mWidthMini, View.MeasureSpec.EXACTLY);\n        }\n        return widthMeasureSpec;\n    }\n\n    public int handleMiniHeight(int heightMeasureSpec, int measuredHeight) {\n        if (View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY\n                && measuredHeight < mHeightMini) {\n            return View.MeasureSpec.makeMeasureSpec(mHeightMini, View.MeasureSpec.EXACTLY);\n        }\n        return heightMeasureSpec;\n    }\n\n    public int getMeasuredWidthSpec(int widthMeasureSpec) {\n        if (mWidthLimit > 0) {\n            int size = View.MeasureSpec.getSize(widthMeasureSpec);\n            if (size > mWidthLimit) {\n                int mode = View.MeasureSpec.getMode(widthMeasureSpec);\n                if (mode == View.MeasureSpec.AT_MOST) {\n                    widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(mWidthLimit, View.MeasureSpec.AT_MOST);\n                } else {\n                    widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(mWidthLimit, View.MeasureSpec.EXACTLY);\n                }\n\n            }\n        }\n        return widthMeasureSpec;\n    }\n\n    public int getMeasuredHeightSpec(int heightMeasureSpec) {\n        if (mHeightLimit > 0) {\n            int size = View.MeasureSpec.getSize(heightMeasureSpec);\n            if (size > mHeightLimit) {\n                int mode = View.MeasureSpec.getMode(heightMeasureSpec);\n                if (mode == View.MeasureSpec.AT_MOST) {\n                    heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(mWidthLimit, View.MeasureSpec.AT_MOST);\n                } else {\n                    heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(mWidthLimit, View.MeasureSpec.EXACTLY);\n                }\n            }\n        }\n        return heightMeasureSpec;\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mBorderColor = borderColor;\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mBorderWidth = borderWidth;\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mOuterNormalColor = color;\n        View owner = mOwner.get();\n        if (owner != null) {\n            owner.invalidate();\n        }\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mTopDividerHeight > 0;\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mRightDividerWidth > 0;\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mBottomDividerHeight > 0;\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLeftDividerWidth > 0;\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mBorderWidth > 0;\n    }\n\n    public void drawDividers(Canvas canvas, int w, int h) {\n        View owner = mOwner.get();\n        if(owner == null){\n            return;\n        }\n        if (mDividerPaint == null &&\n                (mTopDividerHeight > 0 || mBottomDividerHeight > 0 || mLeftDividerWidth > 0 || mRightDividerWidth > 0)) {\n            mDividerPaint = new Paint();\n        }\n        canvas.save();\n        canvas.translate(owner.getScrollX(), owner.getScrollY());\n        if (mTopDividerHeight > 0) {\n            mDividerPaint.setStrokeWidth(mTopDividerHeight);\n            mDividerPaint.setColor(mTopDividerColor);\n            if (mTopDividerAlpha < 255) {\n                mDividerPaint.setAlpha(mTopDividerAlpha);\n            }\n            float y = mTopDividerHeight / 2f;\n            canvas.drawLine(mTopDividerInsetLeft, y, w - mTopDividerInsetRight, y, mDividerPaint);\n        }\n\n        if (mBottomDividerHeight > 0) {\n            mDividerPaint.setStrokeWidth(mBottomDividerHeight);\n            mDividerPaint.setColor(mBottomDividerColor);\n            if (mBottomDividerAlpha < 255) {\n                mDividerPaint.setAlpha(mBottomDividerAlpha);\n            }\n            float y = (float) Math.floor(h - mBottomDividerHeight / 2f);\n            canvas.drawLine(mBottomDividerInsetLeft, y, w - mBottomDividerInsetRight, y, mDividerPaint);\n        }\n\n        if (mLeftDividerWidth > 0) {\n            mDividerPaint.setStrokeWidth(mLeftDividerWidth);\n            mDividerPaint.setColor(mLeftDividerColor);\n            if (mLeftDividerAlpha < 255) {\n                mDividerPaint.setAlpha(mLeftDividerAlpha);\n            }\n            float x = mLeftDividerWidth / 2f;\n            canvas.drawLine(x, mLeftDividerInsetTop, x, h - mLeftDividerInsetBottom, mDividerPaint);\n        }\n\n        if (mRightDividerWidth > 0) {\n            mDividerPaint.setStrokeWidth(mRightDividerWidth);\n            mDividerPaint.setColor(mRightDividerColor);\n            if (mRightDividerAlpha < 255) {\n                mDividerPaint.setAlpha(mRightDividerAlpha);\n            }\n            float x = (float) Math.floor(w - mRightDividerWidth / 2f);\n            canvas.drawLine(x, mRightDividerInsetTop, x, h - mRightDividerInsetBottom, mDividerPaint);\n        }\n        canvas.restore();\n    }\n\n\n    private int getRealRadius(){\n        View owner = mOwner.get();\n        if (owner == null) {\n            return mRadius;\n        }\n        int radius;\n        if(mRadius == RADIUS_OF_HALF_VIEW_HEIGHT){\n            radius = owner.getHeight() /2;\n        }else if(mRadius == RADIUS_OF_HALF_VIEW_WIDTH){\n            radius = owner.getWidth() / 2;\n        }else{\n            radius = mRadius;\n        }\n        return radius;\n    }\n\n    public void dispatchRoundBorderDraw(Canvas canvas) {\n        View owner = mOwner.get();\n        if (owner == null) {\n            return;\n        }\n\n        int radius = getRealRadius();\n        boolean needCheckFakeOuterNormalDraw = radius > 0 && !useFeature() && mOuterNormalColor != 0;\n        boolean needDrawBorder = mBorderWidth > 0 && mBorderColor != 0;\n        if (!needCheckFakeOuterNormalDraw && !needDrawBorder) {\n            return;\n        }\n\n        if (mIsShowBorderOnlyBeforeL && useFeature() && mShadowElevation != 0) {\n            return;\n        }\n\n        int width = owner.getWidth(), height = owner.getHeight();\n        canvas.save();\n        canvas.translate(owner.getScrollX(), owner.getScrollY());\n\n        // react\n        float halfBorderWith = mBorderWidth / 2f;\n        if (mIsOutlineExcludePadding) {\n            mBorderRect.set(\n                    owner.getPaddingLeft() + halfBorderWith,\n                    owner.getPaddingTop() + halfBorderWith,\n                    width - owner.getPaddingRight() - halfBorderWith,\n                    height - owner.getPaddingBottom() - halfBorderWith);\n        } else {\n            mBorderRect.set(halfBorderWith, halfBorderWith,\n                    width- halfBorderWith, height - halfBorderWith);\n        }\n\n        if(mShouldUseRadiusArray){\n            if(mRadiusArray == null){\n                mRadiusArray = new float[8];\n            }\n            if (mHideRadiusSide == HIDE_RADIUS_SIDE_TOP) {\n                mRadiusArray[4] = radius;\n                mRadiusArray[5] = radius;\n                mRadiusArray[6] = radius;\n                mRadiusArray[7] = radius;\n            } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_RIGHT) {\n                mRadiusArray[0] = radius;\n                mRadiusArray[1] = radius;\n                mRadiusArray[6] = radius;\n                mRadiusArray[7] = radius;\n            } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_BOTTOM) {\n                mRadiusArray[0] = radius;\n                mRadiusArray[1] = radius;\n                mRadiusArray[2] = radius;\n                mRadiusArray[3] = radius;\n            } else if (mHideRadiusSide == HIDE_RADIUS_SIDE_LEFT) {\n                mRadiusArray[2] = radius;\n                mRadiusArray[3] = radius;\n                mRadiusArray[4] = radius;\n                mRadiusArray[5] = radius;\n            }\n        }\n\n        if (needCheckFakeOuterNormalDraw) {\n            int layerId = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);\n            canvas.drawColor(mOuterNormalColor);\n            mClipPaint.setColor(mOuterNormalColor);\n            mClipPaint.setStyle(Paint.Style.FILL);\n            mClipPaint.setXfermode(mMode);\n            if (!mShouldUseRadiusArray) {\n                canvas.drawRoundRect(mBorderRect, radius, radius, mClipPaint);\n            } else {\n                drawRoundRect(canvas, mBorderRect, mRadiusArray, mClipPaint);\n            }\n            mClipPaint.setXfermode(null);\n            canvas.restoreToCount(layerId);\n        }\n\n        if (needDrawBorder) {\n            mClipPaint.setColor(mBorderColor);\n            mClipPaint.setStrokeWidth(mBorderWidth);\n            mClipPaint.setStyle(Paint.Style.STROKE);\n            if (mShouldUseRadiusArray) {\n                drawRoundRect(canvas, mBorderRect, mRadiusArray, mClipPaint);\n            } else if (radius <= 0) {\n                canvas.drawRect(mBorderRect, mClipPaint);\n            } else {\n                canvas.drawRoundRect(mBorderRect, radius, radius, mClipPaint);\n            }\n        }\n        canvas.restore();\n    }\n\n    private void drawRoundRect(Canvas canvas, RectF rect, float[] radiusArray, Paint paint) {\n        mPath.reset();\n        mPath.addRoundRect(rect, radiusArray, Path.Direction.CW);\n        canvas.drawPath(mPath, paint);\n\n    }\n\n    public static boolean useFeature() {\n        return Build.VERSION.SDK_INT >= 21;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUILinearLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport androidx.annotation.ColorInt;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.alpha.QMUIAlphaLinearLayout;\n\n/**\n * @author cginechen\n * @date 2017-03-10\n */\n\npublic class QMUILinearLayout extends QMUIAlphaLinearLayout implements IQMUILayout {\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUILinearLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUILinearLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUILinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenPress(false);\n        setChangeAlphaWhenDisable(false);\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUIPriorityLinearLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\nimport com.qmuiteam.qmui.R;\n\nimport java.util.ArrayList;\n\npublic class QMUIPriorityLinearLayout extends QMUILinearLayout {\n    private ArrayList<View> mTempMiniWidthChildList = new ArrayList<>();\n    private ArrayList<View> mTempDisposableChildList = new ArrayList<>();\n\n    public QMUIPriorityLinearLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIPriorityLinearLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int orientation = getOrientation();\n        if (orientation == HORIZONTAL) {\n            handleHorizontal(widthMeasureSpec, heightMeasureSpec);\n        } else {\n            handleVertical(widthMeasureSpec, heightMeasureSpec);\n        }\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n    private void handleHorizontal(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int visibleChildCount = getVisibleChildCount();\n        if (widthMode == MeasureSpec.UNSPECIFIED || visibleChildCount == 0 || widthSize <= 0) {\n            return;\n        }\n        int usedWidth = handlePriorityIncompressible(widthMeasureSpec, heightMeasureSpec);\n        if (usedWidth >= widthSize) {\n            for (View view : mTempMiniWidthChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                view.measure(MeasureSpec.makeMeasureSpec(lp.miniContentProtectionSize, MeasureSpec.AT_MOST), heightMeasureSpec);\n                lp.width = view.getMeasuredWidth();\n            }\n            for (View view : mTempDisposableChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                lp.width = 0;\n                lp.leftMargin = 0;\n                lp.rightMargin = 0;\n            }\n        } else {\n            int usefulWidth = widthSize - usedWidth;\n            int miniNeedWidth = 0, miniWidthChildTotalWidth = 0, marginHor;\n            for (View view : mTempMiniWidthChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                view.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST), heightMeasureSpec);\n                marginHor = lp.leftMargin + lp.rightMargin;\n                miniWidthChildTotalWidth += view.getMeasuredWidth() + marginHor;\n                miniNeedWidth += Math.min(view.getMeasuredWidth(), lp.miniContentProtectionSize) + marginHor;\n            }\n            if (miniNeedWidth >= usefulWidth) {\n                for (View view : mTempMiniWidthChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.width = Math.min(view.getMeasuredWidth(), lp.miniContentProtectionSize);\n                }\n                for (View view : mTempDisposableChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.width = 0;\n                    lp.leftMargin = 0;\n                    lp.rightMargin = 0;\n                }\n            } else if (miniWidthChildTotalWidth < usefulWidth) {\n                // there is a space for disposableChildList\n                if (!mTempDisposableChildList.isEmpty()) {\n                    dispatchSpaceToDisposableChildList(mTempDisposableChildList, widthMeasureSpec, heightMeasureSpec,\n                            usefulWidth - miniWidthChildTotalWidth);\n                }\n            } else {\n                // no space for disposableChild\n                for (View view : mTempDisposableChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.width = 0;\n                    lp.leftMargin = 0;\n                    lp.rightMargin = 0;\n                }\n                if (usefulWidth < miniWidthChildTotalWidth && !mTempMiniWidthChildList.isEmpty()) {\n                    dispatchSpaceToMiniWidthChildList(mTempMiniWidthChildList, usefulWidth, miniWidthChildTotalWidth);\n                }\n            }\n        }\n    }\n\n    private void handleVertical(int widthMeasureSpec, int heightMeasureSpec) {\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        int visibleChildCount = getVisibleChildCount();\n        if (heightMode == MeasureSpec.UNSPECIFIED || visibleChildCount == 0 || heightSize <= 0) {\n            return;\n        }\n        int usedHeight = handlePriorityIncompressible(widthMeasureSpec, heightMeasureSpec);\n        if (usedHeight >= heightSize) {\n            for (View view : mTempMiniWidthChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                view.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(lp.miniContentProtectionSize, MeasureSpec.AT_MOST));\n                lp.height = view.getMeasuredHeight();\n            }\n            for (View view : mTempDisposableChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                lp.height = 0;\n                lp.topMargin = 0;\n                lp.bottomMargin = 0;\n            }\n        } else {\n            int usefulSpace = heightSize - usedHeight;\n            int miniNeedSpace = 0, miniSizeChildTotalLength = 0, marginVer;\n            for (View view : mTempMiniWidthChildList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                view.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST));\n                marginVer = lp.topMargin + lp.bottomMargin;\n                miniSizeChildTotalLength += view.getMeasuredHeight() + marginVer;\n                miniNeedSpace += Math.min(view.getMeasuredHeight(), lp.miniContentProtectionSize) + marginVer;\n            }\n            if (miniNeedSpace >= usefulSpace) {\n                for (View view : mTempMiniWidthChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.height = Math.min(view.getMeasuredHeight(), lp.miniContentProtectionSize);\n                }\n                for (View view : mTempDisposableChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.height = 0;\n                    lp.topMargin = 0;\n                    lp.bottomMargin = 0;\n                }\n            } else if (miniSizeChildTotalLength < usefulSpace) {\n                // there is a space for disposableChildList\n                if (!mTempDisposableChildList.isEmpty()) {\n                    dispatchSpaceToDisposableChildList(mTempDisposableChildList, widthMeasureSpec, heightMeasureSpec,\n                            usefulSpace - miniSizeChildTotalLength);\n                }\n            } else {\n                // no space for disposableChild\n                for (View view : mTempDisposableChildList) {\n                    LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                    lp.height = 0;\n                    lp.topMargin = 0;\n                    lp.bottomMargin = 0;\n                }\n                if (usefulSpace < miniSizeChildTotalLength && !mTempMiniWidthChildList.isEmpty()) {\n                    dispatchSpaceToMiniWidthChildList(mTempMiniWidthChildList, usefulSpace, miniSizeChildTotalLength);\n                }\n            }\n        }\n    }\n\n    private int handlePriorityIncompressible(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();\n        int usedSize = 0;\n        mTempMiniWidthChildList.clear();\n        mTempDisposableChildList.clear();\n        int orientation = getOrientation();\n        for (int i = 0; i < getChildCount(); i++) {\n            View child = getChildAt(i);\n            if (child.getVisibility() == GONE) {\n                continue;\n            }\n            LayoutParams lp = (LayoutParams) child.getLayoutParams();\n            lp.backupOrRestore();\n            int priority = lp.getPriority(orientation);\n            int margin = orientation == HORIZONTAL ? lp.leftMargin + lp.rightMargin :\n                    lp.topMargin + lp.bottomMargin;\n            if (priority == LayoutParams.PRIORITY_INCOMPRESSIBLE) {\n                if (orientation == HORIZONTAL) {\n                    if (lp.width >= 0) {\n                        usedSize += lp.width + margin;\n                    } else {\n                        child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST), heightMeasureSpec);\n                        usedSize += child.getMeasuredWidth() + margin;\n                    }\n                } else {\n                    if (lp.height >= 0) {\n                        usedSize += lp.height + margin;\n                    } else {\n                        child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST));\n                        usedSize += child.getMeasuredHeight() + margin;\n                    }\n                }\n            } else if (priority == LayoutParams.PRIORITY_MINI_CONTENT_PROTECTION) {\n                mTempMiniWidthChildList.add(child);\n            } else {\n                if (lp.weight == 0) {\n                    mTempDisposableChildList.add(child);\n                }\n            }\n        }\n        return usedSize;\n    }\n\n    protected void dispatchSpaceToDisposableChildList(ArrayList<View> childList, int widthMeasureSpec, int heightMeasureSpec, int usefulSpace) {\n\n        for (View view : childList) {\n            LayoutParams lp = (LayoutParams) view.getLayoutParams();\n            if (getOrientation() == HORIZONTAL) {\n                if(usefulSpace <= 0){\n                    lp.leftMargin = 0;\n                    lp.rightMargin = 0;\n                    lp.width = 0;\n                }\n                usefulSpace -= lp.leftMargin - lp.rightMargin;\n                if(usefulSpace > 0){\n                    view.measure(\n                            MeasureSpec.makeMeasureSpec(usefulSpace, MeasureSpec.AT_MOST),\n                            getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height));\n                    if(view.getMeasuredWidth() >= usefulSpace){\n                        lp.width = usefulSpace;\n                        usefulSpace = 0;\n                    }else{\n                        usefulSpace -= view.getMeasuredWidth();\n                    }\n                }else{\n                    lp.leftMargin = 0;\n                    lp.rightMargin = 0;\n                    lp.width = 0;\n                }\n            } else {\n                if(usefulSpace <= 0){\n                    lp.topMargin = 0;\n                    lp.bottomMargin = 0;\n                    lp.height = 0;\n                }\n                usefulSpace -= lp.topMargin - lp.bottomMargin;\n                if(usefulSpace > 0){\n                    view.measure(\n                            getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width),\n                            MeasureSpec.makeMeasureSpec(usefulSpace, MeasureSpec.AT_MOST));\n                    if(view.getMeasuredHeight() >= usefulSpace){\n                        lp.height = usefulSpace;\n                        usefulSpace = 0;\n                    }else{\n                        usefulSpace -= view.getMeasuredHeight();\n                    }\n                }else{\n                    lp.topMargin = 0;\n                    lp.bottomMargin = 0;\n                    lp.height = 0;\n                }\n\n            }\n        }\n    }\n\n    protected void dispatchSpaceToMiniWidthChildList(ArrayList<View> childList, int usefulSpace,\n                                                     int calculateTotalLength) {\n        int extra = calculateTotalLength - usefulSpace;\n        if (extra > 0) {\n            for (View view : childList) {\n                LayoutParams lp = (LayoutParams) view.getLayoutParams();\n                if (getOrientation() == HORIZONTAL) {\n                    float radio = (view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin)\n                            * 1f / calculateTotalLength;\n                    int width = (int) (view.getMeasuredWidth() - extra * radio);\n                    lp.width = Math.max(0, width);\n                } else {\n                    float radio = (view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin)\n                            * 1f / calculateTotalLength;\n                    int height = (int) (view.getMeasuredHeight() - extra * radio);\n                    lp.height = Math.max(0, height);\n                }\n            }\n        }\n    }\n\n    private int getVisibleChildCount() {\n        int childCount = getChildCount();\n        int visibleChildCount = 0;\n        for (int i = 0; i < childCount; i++) {\n            if (getChildAt(i).getVisibility() == VISIBLE) {\n                visibleChildCount++;\n            }\n        }\n        return visibleChildCount;\n    }\n\n    @Override\n    protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {\n        return new LayoutParams(lp);\n    }\n\n    @Override\n    public LinearLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {\n        return new LayoutParams(getContext(), attrs);\n    }\n\n    @Override\n    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n        return p instanceof LayoutParams && super.checkLayoutParams(p);\n    }\n\n    public static class LayoutParams extends LinearLayout.LayoutParams {\n        public static final int PRIORITY_DISPOSABLE = 1;\n        public static final int PRIORITY_MINI_CONTENT_PROTECTION = 2;\n        public static final int PRIORITY_INCOMPRESSIBLE = 3;\n\n        private int priority = PRIORITY_MINI_CONTENT_PROTECTION;\n        private int miniContentProtectionSize = 0;\n\n        private int backupWidth = Integer.MIN_VALUE;\n        private int backupHeight = Integer.MIN_VALUE;\n        private int backupLeftMargin = 0;\n        private int backupRightMargin = 0;\n        private int backupTopMargin = 0;\n        private int backupBottomMargin = 0;\n\n\n        public LayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n            final TypedArray a = c.obtainStyledAttributes(attrs,\n                    R.styleable.QMUIPriorityLinearLayout_Layout);\n            priority = a.getInteger(R.styleable.QMUIPriorityLinearLayout_Layout_qmui_layout_priority,\n                    PRIORITY_MINI_CONTENT_PROTECTION);\n            miniContentProtectionSize = a.getDimensionPixelSize(\n                    R.styleable.QMUIPriorityLinearLayout_Layout_qmui_layout_miniContentProtectionSize,\n                    0);\n            a.recycle();\n        }\n\n        public LayoutParams(int width, int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(int width, int height, float weight) {\n            super(width, height, weight);\n        }\n\n        public LayoutParams(ViewGroup.LayoutParams p) {\n            super(p);\n        }\n\n        public LayoutParams(MarginLayoutParams source) {\n            super(source);\n        }\n\n        @TargetApi(19)\n        public LayoutParams(LinearLayout.LayoutParams source) {\n            super(source);\n        }\n\n        public void setPriority(int priority) {\n            this.priority = priority;\n        }\n\n        public void setMiniContentProtectionSize(int miniContentProtectionSize) {\n            this.miniContentProtectionSize = miniContentProtectionSize;\n        }\n\n        public int getPriority(int orientation) {\n            if (weight > 0) {\n                return PRIORITY_DISPOSABLE;\n            }\n            if (orientation == LinearLayout.HORIZONTAL) {\n                if (width >= 0) {\n                    return PRIORITY_INCOMPRESSIBLE;\n                }\n            } else {\n                if (height >= 0) {\n                    return PRIORITY_INCOMPRESSIBLE;\n                }\n            }\n            return priority;\n        }\n\n        void backupOrRestore() {\n            if (backupWidth == Integer.MIN_VALUE) {\n                backupWidth = width;\n                backupLeftMargin = leftMargin;\n                backupRightMargin = rightMargin;\n            } else {\n                width = backupWidth;\n                leftMargin = backupLeftMargin;\n                rightMargin = backupRightMargin;\n            }\n            if (backupHeight == Integer.MIN_VALUE) {\n                backupHeight = height;\n                backupTopMargin = topMargin;\n                backupBottomMargin = bottomMargin;\n            } else {\n                height = backupHeight;\n                topMargin = backupTopMargin;\n                bottomMargin = backupBottomMargin;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/layout/QMUIRelativeLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.layout;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport androidx.annotation.ColorInt;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.alpha.QMUIAlphaRelativeLayout;\n\n/**\n * @author cginechen\n * @date 2017-03-10\n */\n\npublic class QMUIRelativeLayout extends QMUIAlphaRelativeLayout implements IQMUILayout {\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUIRelativeLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRelativeLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenDisable(false);\n        setChangeAlphaWhenPress(false);\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/link/ITouchableSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.link;\n\nimport android.view.View;\n\n/**\n * @author cginechen\n * @date 2017-03-20\n */\n\npublic interface ITouchableSpan {\n    void setPressed(boolean pressed);\n    void onClick(View widget);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/link/QMUILinkTouchDecorHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.link;\n\nimport android.text.Layout;\nimport android.text.Selection;\nimport android.text.Spannable;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.widget.textview.ISpanTouchFix;\n\nimport java.lang.ref.WeakReference;\n\n/**\n * @author cginechen\n * @date 2017-03-20\n */\n\npublic class QMUILinkTouchDecorHelper {\n    private WeakReference<ITouchableSpan> mPressedSpanRf;\n\n    public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) {\n        if (event.getAction() == MotionEvent.ACTION_DOWN) {\n            ITouchableSpan span = getPressedSpan(textView, spannable, event);\n            if (span != null) {\n                span.setPressed(true);\n                Selection.setSelection(spannable, spannable.getSpanStart(span),\n                        spannable.getSpanEnd(span));\n                mPressedSpanRf = new WeakReference<>(span);\n            }\n            if (textView instanceof ISpanTouchFix) {\n                ISpanTouchFix tv = (ISpanTouchFix) textView;\n                tv.setTouchSpanHit(span != null);\n            }\n            return span != null;\n        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {\n            ITouchableSpan touchedSpan = getPressedSpan(textView, spannable, event);\n            ITouchableSpan recordSpan = null;\n            if (mPressedSpanRf != null){\n                recordSpan = mPressedSpanRf.get();\n            }\n\n            if(recordSpan != null && recordSpan != touchedSpan){\n                recordSpan.setPressed(false);\n                mPressedSpanRf = null;\n                recordSpan = null;\n                Selection.removeSelection(spannable);\n            }\n            if (textView instanceof ISpanTouchFix) {\n                ISpanTouchFix tv = (ISpanTouchFix) textView;\n                tv.setTouchSpanHit(recordSpan != null);\n            }\n            return recordSpan != null;\n        } else if (event.getAction() == MotionEvent.ACTION_UP) {\n            boolean touchSpanHint = false;\n            ITouchableSpan recordSpan = null;\n            if (mPressedSpanRf != null){\n                recordSpan = mPressedSpanRf.get();\n            }\n            if (recordSpan != null) {\n                touchSpanHint = true;\n                recordSpan.setPressed(false);\n                if(event.getAction() == MotionEvent.ACTION_UP){\n                    recordSpan.onClick(textView);\n                }\n            }\n\n            mPressedSpanRf = null;\n            Selection.removeSelection(spannable);\n            if (textView instanceof ISpanTouchFix) {\n                ISpanTouchFix tv = (ISpanTouchFix) textView;\n                tv.setTouchSpanHit(touchSpanHint);\n            }\n            return touchSpanHint;\n        } else {\n            ITouchableSpan recordSpan = null;\n            if (mPressedSpanRf != null){\n                recordSpan = mPressedSpanRf.get();\n            }\n            if (recordSpan != null) {\n                recordSpan.setPressed(false);\n            }\n            if (textView instanceof ISpanTouchFix) {\n                ISpanTouchFix tv = (ISpanTouchFix) textView;\n                tv.setTouchSpanHit(false);\n            }\n            mPressedSpanRf = null;\n            Selection.removeSelection(spannable);\n            return false;\n        }\n\n    }\n\n    public ITouchableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {\n        int x = (int) event.getX();\n        int y = (int) event.getY();\n\n        x -= textView.getTotalPaddingLeft();\n        y -= textView.getTotalPaddingTop();\n\n        x += textView.getScrollX();\n        y += textView.getScrollY();\n\n        Layout layout = textView.getLayout();\n        int line = layout.getLineForVertical(y);\n\n        /*\n         * BugFix: https://issuetracker.google.com/issues/113348914\n         */\n        try {\n            int off = layout.getOffsetForHorizontal(line, x);\n            if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) {\n                // 实际上没点到任何内容\n                off = -1;\n            }\n            ITouchableSpan[] link = spannable.getSpans(off, off, ITouchableSpan.class);\n            ITouchableSpan touchedSpan = null;\n            if (link.length > 0) {\n                touchedSpan = link[0];\n            }\n            return touchedSpan;\n        } catch (IndexOutOfBoundsException e) {\n            if (QMUIConfig.DEBUG) {\n                Log.d(this.toString(), \"getPressedSpan\", e);\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/link/QMUILinkTouchMovementMethod.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.link;\n\nimport android.text.Spannable;\nimport android.text.method.LinkMovementMethod;\nimport android.text.method.MovementMethod;\nimport android.text.method.Touch;\nimport android.view.MotionEvent;\nimport android.widget.TextView;\n\n/**\n * 配合 {@link QMUILinkTouchDecorHelper} 使用\n *\n * @author cginechen\n * @date 2017-03-20\n */\n\npublic class QMUILinkTouchMovementMethod extends LinkMovementMethod {\n\n    @Override\n    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {\n        return sHelper.onTouchEvent(widget, buffer, event)\n                || Touch.onTouchEvent(widget, buffer, event);\n    }\n\n    public static MovementMethod getInstance() {\n        if (sInstance == null)\n            sInstance = new QMUILinkTouchMovementMethod();\n\n        return sInstance;\n    }\n\n    private static QMUILinkTouchMovementMethod sInstance;\n    private static QMUILinkTouchDecorHelper sHelper = new QMUILinkTouchDecorHelper();\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/link/QMUILinkify.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.link;\n\n/*\n * Copyright (C) 2007 The Android Open Source Project\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\nimport android.content.res.ColorStateList;\nimport android.graphics.Color;\nimport android.text.Spannable;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.TextPaint;\nimport android.text.method.LinkMovementMethod;\nimport android.text.method.MovementMethod;\nimport android.text.style.URLSpan;\nimport android.util.Patterns;\nimport android.view.View;\nimport android.webkit.WebView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.span.QMUIOnSpanClickListener;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n\n/**\n * Linkify take a piece of text and a regular expression and turns all of the\n * regex matches in the text into clickable links.  This is particularly\n * useful for matching things like email addresses, web urls, etc. and making\n * them actionable.\n * <p>\n * Alone with the pattern that is to be matched, a url scheme prefix is also\n * required.  Any pattern match that does not begin with the supplied scheme\n * will have the scheme prepended to the matched text when the clickable url\n * is created.  For instance, if you are matching web urls you would supply\n * the scheme <code>http://</code>.  If the pattern matches example.com, which\n * does not have a url scheme prefix, the supplied scheme will be prepended to\n * create <code>http://example.com</code> when the clickable url link is\n * created.\n */\n\npublic class QMUILinkify {\n\n    public static final Pattern WECHAT_PHONE = Pattern.compile(\"\\\\+?(\\\\d{2,8}([- ]?\\\\d{3,8}){2,6}|\\\\d{5,20})\");\n\n    // 其他数字的情况\n    public static final Pattern NOT_PHONE = Pattern.compile(\"^\\\\d+(\\\\.\\\\d+)+(-\\\\d+)*$\");\n\n    private static final String UrlEndAppendNextChars = \"[$]\";\n    /**\n     * Bit field indicating that web URLs should be matched in methods that\n     * take an options mask\n     */\n    public static final int WEB_URLS = 0x01;\n\n    /**\n     * Bit field indicating that email addresses should be matched in methods\n     * that take an options mask\n     */\n    public static final int EMAIL_ADDRESSES = 0x02;\n\n    /**\n     * Bit field indicating that phone numbers should be matched in methods that\n     * take an options mask\n     */\n    public static final int PHONE_NUMBERS = 0x04;\n\n    /**\n     * Bit field indicating that street addresses should be matched in methods that\n     * take an options mask\n     */\n    public static final int MAP_ADDRESSES = 0x08;\n\n    /**\n     * Bit mask indicating that all available patterns should be matched in\n     * methods that take an options mask\n     */\n    public static final int ALL = WEB_URLS | EMAIL_ADDRESSES | PHONE_NUMBERS | MAP_ADDRESSES;\n\n    /**\n     * Don't treat anything with fewer than this many digits as a\n     * phone number.\n     */\n    private static final int PHONE_NUMBER_MINIMUM_DIGITS = 7;\n\n    public static final WebUrlMatcher QMUI_WEB_URL_MATCHER = new WebUrlMatcher() {\n        @Override\n        public Pattern getPattern() {\n            return WebUrlPattern.WEB_URL;\n        }\n    };\n\n    private static WebUrlMatcher sWebUrlMatcher = new WebUrlMatcher() {\n        @Override\n        public Pattern getPattern() {\n            return Patterns.WEB_URL;\n        }\n    };\n\n    public static void useQmuiWebUrlMatcher(){\n        sWebUrlMatcher = QMUI_WEB_URL_MATCHER;\n    }\n\n    public static void setWebUrlMatcher(WebUrlMatcher webUrlMatcher) {\n        sWebUrlMatcher = webUrlMatcher;\n    }\n\n    /**\n     * Filters out web URL matches that occur after an at-sign (@).  This is\n     * to prevent turning the domain name in an email address into a web link.\n     */\n    public static final MatchFilter sUrlMatchFilter = new MatchFilter() {\n        public final boolean acceptMatch(CharSequence s, int start, int end) {\n            try {\n                for (int i = start; i < end; ++i) {\n                    if (s.charAt(i) > 256) {\n                        return false;\n                    }\n                }\n                try {\n                    char nextChar = s.charAt(end);\n                    if (nextChar < 256 && !((0 <= UrlEndAppendNextChars.indexOf(nextChar)) || Character.isWhitespace(nextChar))) {\n                        return false;\n                    }\n                } catch (Exception ignored) {\n\n                }\n                if (start == 0) {\n                    return true;\n                }\n                if (s.charAt(start - 1) == '@') {\n                    return false;\n                }\n            } catch (Exception ignored) {\n\n            }\n\n            return true;\n        }\n    };\n\n    /**\n     * Filters out URL matches that don't have enough digits to be a\n     * phone number.\n     */\n    public static final MatchFilter sPhoneNumberMatchFilter = new MatchFilter() {\n        public final boolean acceptMatch(CharSequence s, int start, int end) {\n            int digitCount = 0;\n\n            for (int i = start; i < end; i++) {\n                if (Character.isDigit(s.charAt(i))) {\n                    digitCount++;\n                    if (digitCount >= PHONE_NUMBER_MINIMUM_DIGITS) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        }\n    };\n\n    /**\n     * Transforms matched phone number text into something suitable\n     * to be used in a tel: URL.  It does this by removing everything\n     * but the digits and plus signs.  For instance:\n     * &apos;+1 (919) 555-1212&apos;\n     * becomes &apos;+19195551212&apos;\n     */\n    public static final TransformFilter sPhoneNumberTransformFilter = new TransformFilter() {\n        public final String transformUrl(final Matcher match, String url) {\n            return Patterns.digitsAndPlusOnly(match);\n        }\n    };\n\n    /**\n     * MatchFilter enables client code to have more control over\n     * what is allowed to match and become a link, and what is not.\n     * <p>\n     * For example:  when matching web urls you would like things like\n     * http://www.example.com to match, as well as just example.com itelf.\n     * However, you would not want to match against the domain in\n     * support@example.com.  So, when matching against a web url pattern you\n     * might also include a MatchFilter that disallows the match if it is\n     * immediately preceded by an at-sign (@).\n     */\n    public interface MatchFilter {\n        /**\n         * Examines the character span matched by the pattern and determines\n         * if the match should be turned into an actionable link.\n         *\n         * @param s     The body of text against which the pattern\n         *              was matched\n         * @param start The index of the first character in s that was\n         *              matched by the pattern - inclusive\n         * @param end   The index of the last character in s that was\n         *              matched - exclusive\n         * @return Whether this match should be turned into a link\n         */\n        boolean acceptMatch(CharSequence s, int start, int end);\n    }\n\n    /**\n     * TransformFilter enables client code to have more control over\n     * how matched patterns are represented as URLs.\n     * <p>\n     * For example:  when converting a phone number such as (919)  555-1212\n     * into a tel: URL the parentheses, white space, and hyphen need to be\n     * removed to produce tel:9195551212.\n     */\n    public interface TransformFilter {\n        /**\n         * Examines the matched text and either passes it through or uses the\n         * data in the Matcher state to produce a replacement.\n         *\n         * @param match The regex matcher state that found this URL text\n         * @param url   The text that was matched\n         * @return The transformed form of the URL\n         */\n        String transformUrl(final Matcher match, String url);\n    }\n\n    /**\n     * Scans the text of the provided Spannable and turns all occurrences\n     * of the link types indicated in the mask into clickable links.\n     * If the mask is nonzero, it also removes any existing URLSpans\n     * attached to the Spannable, to avoid problems if you call it\n     * repeatedly on the same text.\n     */\n    public static boolean addLinks(Spannable text, int mask, ColorStateList linkColor, ColorStateList bgColor, QMUIOnSpanClickListener l) {\n        if (mask == 0) {\n            return false;\n        }\n\n        URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);\n\n        for (int i = old.length - 1; i >= 0; i--) {\n            text.removeSpan(old[i]);\n        }\n\n        ArrayList<LinkSpec> links = new ArrayList<>();\n\n        if ((mask & WEB_URLS) != 0) {\n            gatherLinks(links, text, sWebUrlMatcher.getPattern(),\n                    new String[]{\"http://\", \"https://\", \"rtsp://\"},\n                    sUrlMatchFilter, null);\n        }\n\n        if ((mask & EMAIL_ADDRESSES) != 0) {\n            gatherLinks(links, text, Patterns.EMAIL_ADDRESS,\n                    new String[]{\"mailto:\"},\n                    null, null);\n        }\n\n        if ((mask & PHONE_NUMBERS) != 0) {\n            gatherPhoneLinks(links, text, WECHAT_PHONE, new Pattern[]{NOT_PHONE},\n                    new String[]{\"tel:\"}, sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);\n        }\n\n        if ((mask & MAP_ADDRESSES) != 0) {\n            gatherMapLinks(links, text);\n        }\n\n        pruneOverlaps(links);\n\n        if (links.size() == 0) {\n            return false;\n        }\n\n        for (LinkSpec link : links) {\n            applyLink(link.url, link.start, link.end, text, linkColor, bgColor, l);\n        }\n\n        return true;\n    }\n\n    /**\n     * Scans the text of the provided TextView and turns all occurrences of\n     * the link types indicated in the mask into clickable links.  If matches\n     * are found the movement method for the TextView is set to\n     * LinkMovementMethod.\n     */\n    public static boolean addLinks(TextView text, int mask, ColorStateList linkColor, ColorStateList bgColor, QMUIOnSpanClickListener l) {\n        if (mask == 0) {\n            return false;\n        }\n\n        CharSequence t = text.getText();\n\n        if (t instanceof Spannable) {\n            if (addLinks((Spannable) t, mask, linkColor, bgColor, l)) {\n                addLinkMovementMethod(text);\n                return true;\n            }\n\n            return false;\n        } else {\n            SpannableString s = SpannableString.valueOf(t);\n\n            if (addLinks(s, mask, linkColor, bgColor, l)) {\n                addLinkMovementMethod(text);\n                text.setText(s);\n\n                return true;\n            }\n\n            return false;\n        }\n    }\n\n    private static void addLinkMovementMethod(TextView t) {\n        MovementMethod m = t.getMovementMethod();\n\n        if ((m == null) || !(m instanceof LinkMovementMethod)) {\n            if (t.getLinksClickable()) {\n                t.setMovementMethod(LinkMovementMethod.getInstance());\n            }\n        }\n    }\n\n    /**\n     * Applies a regex to the text of a TextView turning the matches into\n     * links.  If links are found then UrlSpans are applied to the link\n     * text match areas, and the movement method for the text is changed\n     * to LinkMovementMethod.\n     *\n     * @param text    TextView whose text is to be marked-up with links\n     * @param pattern Regex pattern to be used for finding links\n     * @param scheme  Url scheme string (eg <code>http://</code> to be\n     *                prepended to the url of links that do not have\n     *                a scheme specified in the link text\n     */\n    public static void addLinks(TextView text, Pattern pattern, String scheme) {\n        addLinks(text, pattern, scheme, null, null);\n    }\n\n    /**\n     * Applies a regex to the text of a TextView turning the matches into\n     * links.  If links are found then UrlSpans are applied to the link\n     * text match areas, and the movement method for the text is changed\n     * to LinkMovementMethod.\n     *\n     * @param text        TextView whose text is to be marked-up with links\n     * @param p           Regex pattern to be used for finding links\n     * @param scheme      Url scheme string (eg <code>http://</code> to be\n     *                    prepended to the url of links that do not have\n     *                    a scheme specified in the link text\n     * @param matchFilter The filter that is used to allow the client code\n     *                    additional control over which pattern matches are\n     *                    to be converted into links.\n     */\n    public static void addLinks(TextView text, Pattern p, String scheme,\n                                MatchFilter matchFilter, TransformFilter transformFilter) {\n        SpannableString s = SpannableString.valueOf(text.getText());\n\n        if (addLinks(s, p, scheme, matchFilter, transformFilter)) {\n            text.setText(s);\n            addLinkMovementMethod(text);\n        }\n    }\n\n    /**\n     * Applies a regex to a Spannable turning the matches into\n     * links.\n     *\n     * @param text    Spannable whose text is to be marked-up with\n     *                links\n     * @param pattern Regex pattern to be used for finding links\n     * @param scheme  Url scheme string (eg <code>http://</code> to be\n     *                prepended to the url of links that do not have\n     *                a scheme specified in the link text\n     */\n    public static boolean addLinks(Spannable text, Pattern pattern, String scheme) {\n        return addLinks(text, pattern, scheme, null, null);\n    }\n\n    /**\n     * Applies a regex to a Spannable turning the matches into\n     * links.\n     *\n     * @param s           Spannable whose text is to be marked-up with\n     *                    links\n     * @param p           Regex pattern to be used for finding links\n     * @param scheme      Url scheme string (eg <code>http://</code> to be\n     *                    prepended to the url of links that do not have\n     *                    a scheme specified in the link text\n     * @param matchFilter The filter that is used to allow the client code\n     *                    additional control over which pattern matches are\n     *                    to be converted into links.\n     */\n    public static boolean addLinks(Spannable s, Pattern p,\n                                   String scheme, MatchFilter matchFilter,\n                                   TransformFilter transformFilter) {\n        boolean hasMatches = false;\n        String prefix = (scheme == null) ? \"\" : scheme.toLowerCase(Locale.ROOT);\n        Matcher m = p.matcher(s);\n\n        while (m.find()) {\n            int start = m.start();\n            int end = m.end();\n            boolean allowed = true;\n\n            if (matchFilter != null) {\n                allowed = matchFilter.acceptMatch(s, start, end);\n            }\n\n            if (allowed) {\n                String url = makeUrl(m.group(0), new String[]{prefix},\n                        m, transformFilter);\n\n                applyLink(url, start, end, s, null, null, null);\n                hasMatches = true;\n            }\n        }\n\n        return hasMatches;\n    }\n\n    private static void applyLink(String url, int start, int end, Spannable text, final ColorStateList linkColor, final ColorStateList bgColor, QMUIOnSpanClickListener l) {\n        text.setSpan(new StyleableURLSpan(url, l) {\n\n            @Override\n            public void updateDrawState(TextPaint ds) {\n                if (linkColor != null) {\n                    int normalLinkColor = linkColor.getColorForState(new int[]{android.R.attr.state_enabled, -android.R.attr.state_pressed}, Color.TRANSPARENT);\n                    int pressedLinkColor = linkColor.getColorForState(new int[]{android.R.attr.state_pressed}, normalLinkColor);\n                    ds.linkColor = mPressed ? pressedLinkColor : normalLinkColor;\n                }\n                if (bgColor != null) {\n                    int normalBgColor = bgColor.getColorForState(new int[]{android.R.attr.state_enabled, -android.R.attr.state_pressed}, Color.TRANSPARENT);\n                    int pressedBgColor = bgColor.getColorForState(new int[]{android.R.attr.state_pressed}, normalBgColor);\n                    ds.bgColor = mPressed ? pressedBgColor : normalBgColor;\n                }\n                super.updateDrawState(ds);\n                ds.setUnderlineText(false);\n            }\n\n        }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n    }\n\n    private static abstract class StyleableURLSpan extends URLSpan implements ITouchableSpan {\n\n        protected boolean mPressed = false;\n        protected String mUrl;\n        protected QMUIOnSpanClickListener mOnSpanClickListener;\n\n        public StyleableURLSpan(String url, QMUIOnSpanClickListener l) {\n            super(url);\n            mUrl = url;\n            mOnSpanClickListener = l;\n        }\n\n        @Override\n        public void setPressed(boolean pressed) {\n            mPressed = pressed;\n        }\n\n        @Override\n        public void onClick(View widget) {\n            if (mOnSpanClickListener.onSpanClick(mUrl)) {\n                return;\n            }\n            super.onClick(widget);\n        }\n\n    }\n\n    private static String makeUrl(String url, String[] prefixes,\n                                  Matcher m, TransformFilter filter) {\n        if (filter != null) {\n            url = filter.transformUrl(m, url);\n        }\n\n        boolean hasPrefix = false;\n\n        for (String prefixe : prefixes) {\n            if (url.regionMatches(true, 0, prefixe, 0,\n                    prefixe.length())) {\n                hasPrefix = true;\n\n                // Fix capitalization if necessary\n                if (!url.regionMatches(false, 0, prefixe, 0,\n                        prefixe.length())) {\n                    url = prefixe + url.substring(prefixe.length());\n                }\n\n                break;\n            }\n        }\n\n        if (!hasPrefix) {\n            url = prefixes[0] + url;\n        }\n\n        return url;\n    }\n\n    private static void gatherLinks(ArrayList<LinkSpec> links,\n                                    Spannable s, Pattern pattern, String[] schemes,\n                                    MatchFilter matchFilter, TransformFilter transformFilter) {\n        Matcher m = pattern.matcher(s);\n\n        while (m.find()) {\n            int start = m.start();\n            int end = m.end();\n\n            if (matchFilter == null || matchFilter.acceptMatch(s, start, end)) {\n                LinkSpec spec = new LinkSpec();\n\n                spec.url = makeUrl(m.group(0), schemes, m, transformFilter);\n                spec.start = start;\n                spec.end = end;\n\n                links.add(spec);\n            }\n        }\n    }\n\n    private static void gatherPhoneLinks(ArrayList<LinkSpec> links,\n                                         Spannable s, Pattern pattern, Pattern[] excepts, String[] schemes,\n                                         MatchFilter matchFilter, TransformFilter transformFilter) {\n        Matcher m = pattern.matcher(s);\n\n        while (m.find()) {\n            if (isInExcepts(m.group(), excepts)) {\n                continue;\n            }\n\n            int start = m.start();\n            int end = m.end();\n\n            if (matchFilter == null || matchFilter.acceptMatch(s, start, end)) {\n                LinkSpec spec = new LinkSpec();\n\n                spec.url = makeUrl(m.group(0), schemes, m, transformFilter);\n                spec.start = start;\n                spec.end = end;\n\n                links.add(spec);\n            }\n        }\n    }\n\n    private static boolean isInExcepts(CharSequence data, Pattern[] excepts) {\n        for (Pattern except : excepts) {\n            Matcher m = except.matcher(data);\n            if (m.find()) {\n                return true;\n            }\n        }\n\n        return isTooLarge(data);\n    }\n\n    private final static int MAX_NUMBER = 21;\n\n    private static boolean isTooLarge(CharSequence data) {\n        if (data.length() <= MAX_NUMBER) {\n            return false;\n        }\n\n        final int count = data.length();\n        int digitCount = 0;\n        for (int i = 0; i < count; i++) {\n            if (Character.isDigit(data.charAt(i))) {\n                digitCount++;\n                if (digitCount > MAX_NUMBER) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n//    private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {\n//        PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();\n//        Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),\n//                Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);\n//        for (PhoneNumberMatch match : matches) {\n//            LinkSpec spec = new LinkSpec();\n//            spec.url = \"tel:\" + PhoneNumberUtils.normalizeNumber(match.rawString());\n//            spec.start = match.start();\n//            spec.end = match.end();\n//            links.add(spec);\n//        }\n//    }\n\n    private static void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {\n        String string = s.toString();\n        String address;\n        int base = 0;\n\n        try {\n            while ((address = WebView.findAddress(string)) != null) {\n                int start = string.indexOf(address);\n\n                if (start < 0) {\n                    break;\n                }\n\n                LinkSpec spec = new LinkSpec();\n                int length = address.length();\n                int end = start + length;\n\n                spec.start = base + start;\n                spec.end = base + end;\n                string = string.substring(end);\n                base += end;\n\n                String encodedAddress;\n\n                try {\n                    encodedAddress = URLEncoder.encode(address, \"UTF-8\");\n                } catch (UnsupportedEncodingException e) {\n                    continue;\n                }\n\n                spec.url = \"geo:0,0?q=\" + encodedAddress;\n                links.add(spec);\n            }\n        } catch (UnsupportedOperationException e) {\n            // findAddress may fail with an unsupported exception on platforms without a WebView.\n            // In this case, we will not append anything to the links variable: it would have died\n            // in WebView.findAddress.\n        }\n    }\n\n    private static void pruneOverlaps(ArrayList<LinkSpec> links) {\n        Comparator<LinkSpec> c = new Comparator<LinkSpec>() {\n            public final int compare(LinkSpec a, LinkSpec b) {\n                if (a.start < b.start) {\n                    return -1;\n                }\n\n                if (a.start > b.start) {\n                    return 1;\n                }\n\n                if (a.end < b.end) {\n                    return 1;\n                }\n\n                if (a.end > b.end) {\n                    return -1;\n                }\n\n                return 0;\n            }\n        };\n\n        Collections.sort(links, c);\n\n        int len = links.size();\n        int i = 0;\n\n        while (i < len - 1) {\n            LinkSpec a = links.get(i);\n            LinkSpec b = links.get(i + 1);\n            int remove = -1;\n\n            if ((a.start <= b.start) && (a.end > b.start)) {\n                if (b.end <= a.end) {\n                    remove = i + 1;\n                } else if ((a.end - a.start) > (b.end - b.start)) {\n                    remove = i + 1;\n                } else if ((a.end - a.start) < (b.end - b.start)) {\n                    remove = i;\n                }\n\n                if (remove != -1) {\n                    links.remove(remove);\n                    len--;\n                    continue;\n                }\n\n            }\n\n            i++;\n        }\n    }\n\n    private static class LinkSpec {\n        String url;\n        int start;\n        int end;\n    }\n\n    private static class WebUrlPattern {\n\n        // all domain names\n        private static final String[] EXT = {\n                \"top\", \"com\", \"net\", \"org\", \"edu\", \"gov\", \"int\", \"mil\", \"tel\", \"biz\", \"cc\", \"tv\", \"info\", \"zw\",\n                \"name\", \"hk\", \"mobi\", \"asia\", \"cd\", \"travel\", \"pro\", \"museum\", \"coop\", \"aero\", \"ad\", \"ae\", \"af\",\n                \"ag\", \"ai\", \"al\", \"am\", \"an\", \"ao\", \"aq\", \"ar\", \"as\", \"at\", \"au\", \"aw\", \"az\", \"ba\", \"bb\", \"bd\",\n                \"be\", \"bf\", \"bg\", \"bh\", \"bi\", \"bj\", \"bm\", \"bn\", \"bo\", \"br\", \"bs\", \"bt\", \"bv\", \"bw\", \"by\", \"bz\",\n                \"ca\", \"cc\", \"cf\", \"cg\", \"ch\", \"ci\", \"ck\", \"cl\", \"cm\", \"cn\", \"co\", \"cq\", \"cr\", \"cu\", \"cv\", \"cx\",\n                \"cy\", \"cz\", \"de\", \"dj\", \"dk\", \"dm\", \"do\", \"dz\", \"ec\", \"ee\", \"eg\", \"eh\", \"es\", \"et\", \"ev\", \"fi\",\n                \"fj\", \"fk\", \"fm\", \"fo\", \"fr\", \"ga\", \"gb\", \"gd\", \"ge\", \"gf\", \"gh\", \"gi\", \"gl\", \"gm\", \"gn\", \"gp\",\n                \"gr\", \"gt\", \"gu\", \"gw\", \"gy\", \"hk\", \"hm\", \"hn\", \"hr\", \"ht\", \"hu\", \"id\", \"ie\", \"il\", \"in\", \"io\",\n                \"iq\", \"ir\", \"is\", \"it\", \"jm\", \"jo\", \"jp\", \"ke\", \"kg\", \"kh\", \"ki\", \"km\", \"kn\", \"kp\", \"kr\", \"kw\",\n                \"ky\", \"kz\", \"la\", \"lb\", \"lc\", \"li\", \"lk\", \"lr\", \"ls\", \"lt\", \"lu\", \"lv\", \"ly\", \"ma\", \"mc\", \"md\",\n                \"mg\", \"mh\", \"ml\", \"mm\", \"mn\", \"mo\", \"mp\", \"mq\", \"mr\", \"ms\", \"mt\", \"mv\", \"mw\", \"mx\", \"my\", \"mz\",\n                \"na\", \"nc\", \"ne\", \"nf\", \"ng\", \"ni\", \"nl\", \"no\", \"np\", \"nr\", \"nt\", \"nu\", \"nz\", \"om\", \"qa\", \"pa\",\n                \"pe\", \"pf\", \"pg\", \"ph\", \"pk\", \"pl\", \"pm\", \"pn\", \"pr\", \"pt\", \"pw\", \"py\", \"re\", \"ro\", \"ru\", \"rw\",\n                \"sa\", \"sb\", \"sc\", \"sd\", \"se\", \"sg\", \"sh\", \"si\", \"sj\", \"sk\", \"sl\", \"sm\", \"sn\", \"so\", \"sr\", \"st\",\n                \"su\", \"sy\", \"sz\", \"tc\", \"td\", \"tf\", \"tg\", \"th\", \"tj\", \"tk\", \"tm\", \"tn\", \"to\", \"tp\", \"tr\", \"tt\",\n                \"tv\", \"tw\", \"tz\", \"ua\", \"ug\", \"uk\", \"us\", \"uy\", \"va\", \"vc\", \"ve\", \"vg\", \"vn\", \"vu\", \"wf\", \"ws\",\n                \"ye\", \"yu\", \"za\", \"zm\", \"zr\"\n        };\n\n        private static final String PROTOCOL = \"(?i:http|https|rtsp)://\";\n        private static final String IP_ADDRESS =\n                \"((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\\\.(25[0-5]|2[0-4]\"\n                        + \"[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\\\.(25[0-5]|2[0-4][0-9]|[0-1]\"\n                        + \"[0-9]{2}|[1-9][0-9]|[1-9]|0)\\\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}\"\n                        + \"|[1-9][0-9]|[0-9]))\";\n        /**\n         * Valid UCS characters defined in RFC 3987. Excludes space characters.\n         */\n        private static final String UCS_CHAR = \"[\" +\n                \"\\u00A0-\\uD7FF\" +\n                \"\\uF900-\\uFDCF\" +\n                \"\\uFDF0-\\uFFEF\" +\n                \"\\uD800\\uDC00-\\uD83F\\uDFFD\" +\n                \"\\uD840\\uDC00-\\uD87F\\uDFFD\" +\n                \"\\uD880\\uDC00-\\uD8BF\\uDFFD\" +\n                \"\\uD8C0\\uDC00-\\uD8FF\\uDFFD\" +\n                \"\\uD900\\uDC00-\\uD93F\\uDFFD\" +\n                \"\\uD940\\uDC00-\\uD97F\\uDFFD\" +\n                \"\\uD980\\uDC00-\\uD9BF\\uDFFD\" +\n                \"\\uD9C0\\uDC00-\\uD9FF\\uDFFD\" +\n                \"\\uDA00\\uDC00-\\uDA3F\\uDFFD\" +\n                \"\\uDA40\\uDC00-\\uDA7F\\uDFFD\" +\n                \"\\uDA80\\uDC00-\\uDABF\\uDFFD\" +\n                \"\\uDAC0\\uDC00-\\uDAFF\\uDFFD\" +\n                \"\\uDB00\\uDC00-\\uDB3F\\uDFFD\" +\n                \"\\uDB44\\uDC00-\\uDB7F\\uDFFD\" +\n                \"&&[^\\u00A0[\\u2000-\\u200A]\\u2028\\u2029\\u202F\\u3000]]\";\n\n        /**\n         * Valid characters for IRI label defined in RFC 3987.\n         */\n        private static final String LABEL_CHAR = \"a-zA-Z0-9\" + UCS_CHAR;\n\n        private static final String PORT_NUMBER = \"\\\\:\\\\d{1,5}\";\n        private static final String PATH_AND_QUERY = \"[/\\\\?](?:(?:[\" + LABEL_CHAR\n                + \";/\\\\?:@&=#~\"  // plus optional query params\n                + \"\\\\-\\\\.\\\\+!\\\\*'\\\\(\\\\),_\\\\$])|(?:%[a-fA-F0-9]{2}))*\";\n        private static Pattern WEB_URL;\n\n\n\n        static {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"(\");\n            for (int i = 0; i < EXT.length; i++) {\n                if(i != 0){\n                    sb.append(\"|\");\n                }\n                sb.append(EXT[i]);\n            }\n            sb.append(\")\");\n\n            String host = \"((?:(www\\\\.|[a-zA-Z\\\\.\\\\-]+\\\\.)?[a-zA-Z0-9\\\\-]+)\" + \"\\\\.\" + sb.toString() + \")\";\n            WEB_URL = Pattern.compile(\"(\"\n                    + \"(\" + PROTOCOL + \")?\"\n                    + \"(\" + IP_ADDRESS + \"|\" + host +\")\"\n                    + \"(\" + PORT_NUMBER + \")?\"\n                    + \"(\" + PATH_AND_QUERY + \")?\"\n                    + \")\");\n        }\n    }\n\n    public interface WebUrlMatcher {\n        Pattern getPattern();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/link/QMUIScrollingMovementMethod.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.link;\n\nimport android.text.Spannable;\nimport android.text.method.MovementMethod;\nimport android.text.method.ScrollingMovementMethod;\nimport android.text.method.Touch;\nimport android.view.MotionEvent;\nimport android.widget.TextView;\n\n/**\n * @author cginechen\n * @date 2017-03-20\n */\n\npublic class QMUIScrollingMovementMethod extends ScrollingMovementMethod {\n\n    @Override\n    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {\n        return sHelper.onTouchEvent(widget, buffer, event)\n                || Touch.onTouchEvent(widget, buffer, event);\n    }\n\n    public static MovementMethod getInstance() {\n        if (sInstance == null)\n            sInstance = new QMUIScrollingMovementMethod();\n\n        return sInstance;\n    }\n\n    private static QMUIScrollingMovementMethod sInstance;\n    private static QMUILinkTouchDecorHelper sHelper = new QMUILinkTouchDecorHelper();\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/IQMUIContinuousNestedBottomView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\npublic interface IQMUIContinuousNestedBottomView extends IQMUIContinuousNestedScrollCommon {\n    int HEIGHT_IS_ENOUGH_TO_SCROLL = -1;\n\n    /**\n     * consume scroll\n     *\n     * @param dyUnconsumed the delta value to consume\n     */\n    void consumeScroll(int dyUnconsumed);\n\n    void smoothScrollYBy(int dy, int duration);\n\n    void stopScroll();\n\n    /**\n     * sometimes the content of BottomView is not enough to scroll,\n     * so BottomView should tell the this info to  {@link QMUIContinuousNestedScrollLayout}\n     *\n     * @return {@link #HEIGHT_IS_ENOUGH_TO_SCROLL} if can scroll, or content height.\n     */\n    int getContentHeight();\n\n    int getCurrentScroll();\n\n    int getScrollOffsetRange();\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/IQMUIContinuousNestedScrollCommon.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.os.Bundle;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic interface IQMUIContinuousNestedScrollCommon {\n\n    int SCROLL_STATE_IDLE = RecyclerView.SCROLL_STATE_IDLE;\n    int SCROLL_STATE_DRAGGING = RecyclerView.SCROLL_STATE_DRAGGING;\n    int SCROLL_STATE_SETTLING = RecyclerView.SCROLL_STATE_SETTLING;\n\n    void saveScrollInfo(@NonNull Bundle bundle);\n\n    void restoreScrollInfo(@NonNull Bundle bundle);\n\n    void injectScrollNotifier(OnScrollNotifier notifier);\n\n    interface OnScrollNotifier {\n        void notify(int innerOffset, int innerRange);\n\n        void onScrollStateChange(View view, int newScrollState);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/IQMUIContinuousNestedTopView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\npublic interface IQMUIContinuousNestedTopView extends IQMUIContinuousNestedScrollCommon {\n    /**\n     * consume scroll\n     *\n     * @param dyUnconsumed the delta value to consume\n     * @return the remain unconsumed value\n     */\n    int consumeScroll(int dyUnconsumed);\n\n    int getCurrentScroll();\n\n    int getScrollOffsetRange();\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedBottomAreaBehavior.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.GravityCompat;\n\npublic class QMUIContinuousNestedBottomAreaBehavior extends QMUIViewOffsetBehavior<View> {\n\n    private final Rect tempRect1 = new Rect();\n    private final Rect tempRect2 = new Rect();\n\n    private int mTopInset = 0;\n\n    public void setTopInset(int topInset) {\n        mTopInset = topInset;\n    }\n\n    public QMUIContinuousNestedBottomAreaBehavior() {\n    }\n\n    public QMUIContinuousNestedBottomAreaBehavior(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {\n        final int childLpHeight = child.getLayoutParams().height;\n        if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT\n                || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {\n\n            int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);\n            if (availableHeight == 0) {\n                availableHeight = parent.getHeight();\n            }\n\n            availableHeight -= mTopInset;\n\n            final int heightMeasureSpec =\n                    View.MeasureSpec.makeMeasureSpec(\n                            availableHeight,\n                            childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT\n                                    ? View.MeasureSpec.EXACTLY\n                                    : View.MeasureSpec.AT_MOST);\n\n            parent.onMeasureChild(\n                    child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);\n\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    protected void layoutChild(CoordinatorLayout parent, View child, int layoutDirection) {\n        List<View> dependencies = parent.getDependencies(child);\n        if (!dependencies.isEmpty()) {\n            View topView = dependencies.get(0);\n            final CoordinatorLayout.LayoutParams lp =\n                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();\n            final Rect available = tempRect1;\n            available.set(\n                    parent.getPaddingLeft() + lp.leftMargin,\n                    topView.getBottom() + lp.topMargin,\n                    parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,\n                    parent.getHeight() + topView.getBottom() - parent.getPaddingBottom() - lp.bottomMargin);\n\n            final Rect out = tempRect2;\n            GravityCompat.apply(\n                    resolveGravity(lp.gravity),\n                    child.getMeasuredWidth(),\n                    child.getMeasuredHeight(),\n                    available,\n                    out,\n                    layoutDirection);\n\n            child.layout(out.left, out.top, out.right, out.bottom);\n        } else {\n            super.layoutChild(parent, child, layoutDirection);\n        }\n    }\n\n    @Override\n    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {\n        boolean ret = super.onLayoutChild(parent, child, layoutDirection);\n        List<View> dependencies = parent.getDependencies(child);\n        if (!dependencies.isEmpty()) {\n            View topView = dependencies.get(0);\n            setTopAndBottomOffset(topView.getBottom() - getLayoutTop());\n        }\n        return ret;\n    }\n\n    private static int resolveGravity(int gravity) {\n        return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;\n    }\n\n    @Override\n    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {\n        return dependency instanceof IQMUIContinuousNestedTopView;\n    }\n\n    @Override\n    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {\n        setTopAndBottomOffset(dependency.getBottom() - getLayoutTop());\n        return false;\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedBottomDelegateLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.animation.Interpolator;\nimport android.widget.FrameLayout;\nimport android.widget.OverScroller;\n\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.NestedScrollingChild;\nimport androidx.core.view.NestedScrollingChild2;\nimport androidx.core.view.NestedScrollingChildHelper;\nimport androidx.core.view.NestedScrollingParent2;\nimport androidx.core.view.NestedScrollingParentHelper;\nimport androidx.core.view.ViewCompat;\n\nimport static com.qmuiteam.qmui.QMUIInterpolatorStaticHolder.QUNITIC_INTERPOLATOR;\n\n\npublic abstract class QMUIContinuousNestedBottomDelegateLayout extends QMUIFrameLayout implements\n        NestedScrollingChild2, NestedScrollingParent2, IQMUIContinuousNestedBottomView {\n    public static final String KEY_SCROLL_INFO_OFFSET = \"@qmui_scroll_info_bottom_dl_offset\";\n\n    private final NestedScrollingParentHelper mParentHelper;\n    private final NestedScrollingChildHelper mChildHelper;\n    private View mHeaderView;\n    private View mContentView;\n    private QMUIViewOffsetHelper mHeaderViewOffsetHelper;\n    private QMUIViewOffsetHelper mContentViewOffsetHelper;\n    private IQMUIContinuousNestedBottomView.OnScrollNotifier mOnScrollNotifier;\n\n    private static final int INVALID_POINTER = -1;\n    private boolean isBeingDragged;\n    private int activePointerId = INVALID_POINTER;\n    private int lastMotionY;\n    private int touchSlop = -1;\n    private VelocityTracker velocityTracker;\n    private final ViewFlinger mViewFlinger;\n    private final int[] mScrollConsumed = new int[2];\n    private final int[] mScrollOffset = new int[2];\n    private Rect mTempRect = new Rect();\n    private int mNestedOffsetY = 0;\n    private Runnable mCheckLayoutAction = new Runnable() {\n        @Override\n        public void run() {\n            checkLayout();\n        }\n    };\n\n    public QMUIContinuousNestedBottomDelegateLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIContinuousNestedBottomDelegateLayout(Context context, AttributeSet attrs) {\n        this(context, null, 0);\n    }\n\n    public QMUIContinuousNestedBottomDelegateLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        mParentHelper = new NestedScrollingParentHelper(this);\n        mChildHelper = new NestedScrollingChildHelper(this);\n\n        ViewCompat.setNestedScrollingEnabled(this, true);\n        mHeaderView = onCreateHeaderView();\n        mContentView = onCreateContentView();\n        if (!(mContentView instanceof IQMUIContinuousNestedBottomView)) {\n            throw new IllegalStateException(\"the view create by onCreateContentView() \" +\n                    \"should implement from IQMUIContinuousNestedBottomView\");\n        }\n        addView(mHeaderView, new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, getHeaderHeightLayoutParam()));\n        addView(mContentView, new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        mHeaderViewOffsetHelper = new QMUIViewOffsetHelper(mHeaderView);\n        mContentViewOffsetHelper = new QMUIViewOffsetHelper(mContentView);\n        mViewFlinger = new ViewFlinger();\n    }\n\n    public View getHeaderView() {\n        return mHeaderView;\n    }\n\n    public View getContentView() {\n        return mContentView;\n    }\n\n    public int getOffsetCurrent() {\n        return -mHeaderViewOffsetHelper.getTopAndBottomOffset();\n    }\n\n    public int getOffsetRange() {\n        return -getMiniOffset();\n    }\n\n    private int getMiniOffset() {\n        IQMUIContinuousNestedBottomView b = (IQMUIContinuousNestedBottomView) mContentView;\n        int contentHeight = b.getContentHeight();\n        FrameLayout.LayoutParams headerLp = (LayoutParams) mHeaderView.getLayoutParams();\n        int minOffset = -mHeaderView.getHeight() - headerLp.bottomMargin + getHeaderStickyHeight();\n        if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n            minOffset += mContentView.getHeight() - contentHeight;\n            minOffset = Math.min(minOffset, 0);\n        }\n        return minOffset;\n    }\n\n    @Override\n    public int getContentHeight() {\n        IQMUIContinuousNestedBottomView b = (IQMUIContinuousNestedBottomView) mContentView;\n        int bc = b.getContentHeight();\n        if (bc == IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL || bc > mContentView.getHeight()) {\n            return IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL;\n        }\n        int bottomMargin = getContentBottomMargin();\n        if (bc + mHeaderView.getHeight() + bottomMargin > getHeight()) {\n            return IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL;\n        }\n        return mHeaderView.getHeight() + bc + bottomMargin;\n    }\n\n    @NonNull\n    protected abstract View onCreateHeaderView();\n\n    @NonNull\n    protected abstract View onCreateContentView();\n\n    protected int getHeaderStickyHeight() {\n        return 0;\n    }\n\n\n    protected int getHeaderHeightLayoutParam() {\n        return ViewGroup.LayoutParams.WRAP_CONTENT;\n    }\n\n    protected int getContentBottomMargin() {\n        return 0;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        mContentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                heightSize - getHeaderStickyHeight() - getContentBottomMargin(),\n                MeasureSpec.EXACTLY));\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        mHeaderView.layout(0, 0, mHeaderView.getMeasuredWidth(),\n                mHeaderView.getMeasuredHeight());\n\n\n        int contentTop = mHeaderView.getBottom();\n        mContentView.layout(0, contentTop, mContentView.getMeasuredWidth(),\n                contentTop + mContentView.getMeasuredHeight());\n\n        mHeaderViewOffsetHelper.onViewLayout();\n        mContentViewOffsetHelper.onViewLayout();\n        postCheckLayout();\n    }\n\n    public void postCheckLayout() {\n        removeCallbacks(mCheckLayoutAction);\n        post(mCheckLayoutAction);\n    }\n\n    public void checkLayout() {\n        int offsetCurrent = getOffsetCurrent();\n        int offsetRange = getOffsetRange();\n        IQMUIContinuousNestedBottomView bottomView = (IQMUIContinuousNestedBottomView) mContentView;\n        if (offsetCurrent < offsetRange && bottomView.getCurrentScroll() > 0) {\n            bottomView.consumeScroll(Integer.MIN_VALUE);\n        }\n    }\n\n    private int offsetBy(int dyUnConsumed) {\n        int canConsume = 0;\n\n\n        FrameLayout.LayoutParams headerLp = (LayoutParams) mHeaderView.getLayoutParams();\n        int minOffset = getMiniOffset();\n        if (dyUnConsumed > 0) {\n            canConsume = Math.min(mHeaderView.getTop() - minOffset, dyUnConsumed);\n        } else if (dyUnConsumed < 0) {\n            canConsume = Math.max(mHeaderView.getTop() - headerLp.topMargin, dyUnConsumed);\n        }\n        if (canConsume != 0) {\n            mHeaderViewOffsetHelper.setTopAndBottomOffset(mHeaderViewOffsetHelper.getTopAndBottomOffset() - canConsume);\n            mContentViewOffsetHelper.setTopAndBottomOffset(mContentViewOffsetHelper.getTopAndBottomOffset() - canConsume);\n        }\n        mOnScrollNotifier.notify(-mHeaderViewOffsetHelper.getTopAndBottomOffset(),\n                mHeaderView.getHeight() + ((IQMUIContinuousNestedBottomView) mContentView).getScrollOffsetRange());\n        return dyUnConsumed - canConsume;\n    }\n\n\n    @Override\n    public void consumeScroll(int dy) {\n        if (dy == Integer.MAX_VALUE) {\n            offsetBy(dy);\n            ((IQMUIContinuousNestedBottomView) mContentView).consumeScroll(Integer.MAX_VALUE);\n            return;\n        } else if (dy == Integer.MIN_VALUE) {\n            ((IQMUIContinuousNestedBottomView) mContentView).consumeScroll(Integer.MIN_VALUE);\n            offsetBy(dy);\n            return;\n        }\n        ((IQMUIContinuousNestedBottomView) mContentView).consumeScroll(dy);\n    }\n\n    @Override\n    public void smoothScrollYBy(int dy, int duration) {\n        ((IQMUIContinuousNestedBottomView) mContentView).smoothScrollYBy(dy, duration);\n    }\n\n    @Override\n    public void stopScroll() {\n        ((IQMUIContinuousNestedBottomView) mContentView).stopScroll();\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        return -mHeaderViewOffsetHelper.getTopAndBottomOffset() +\n                ((IQMUIContinuousNestedBottomView) mContentView).getCurrentScroll();\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        if (getContentHeight() != HEIGHT_IS_ENOUGH_TO_SCROLL) {\n            return 0;\n        }\n        return mHeaderView.getHeight() - getHeaderStickyHeight() +\n                ((IQMUIContinuousNestedBottomView) mContentView).getScrollOffsetRange();\n    }\n\n    @Override\n    public void injectScrollNotifier(final OnScrollNotifier notifier) {\n        mOnScrollNotifier = notifier;\n        if (mContentView instanceof IQMUIContinuousNestedBottomView) {\n            ((IQMUIContinuousNestedBottomView) mContentView).injectScrollNotifier(new OnScrollNotifier() {\n                @Override\n                public void notify(int innerOffset, int innerRange) {\n                    notifier.notify(innerOffset - mHeaderView.getTop(),\n                            innerRange + mHeaderView.getHeight());\n                }\n\n                @Override\n                public void onScrollStateChange(View view, int newScrollState) {\n                    notifier.onScrollStateChange(view, newScrollState);\n                }\n            });\n        }\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        bundle.putInt(KEY_SCROLL_INFO_OFFSET, mHeaderViewOffsetHelper.getTopAndBottomOffset());\n        if (mContentView != null) {\n            ((IQMUIContinuousNestedBottomView) mContentView).saveScrollInfo(bundle);\n        }\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n        int offset = bundle.getInt(KEY_SCROLL_INFO_OFFSET, 0);\n        offset = QMUILangHelper.constrain(offset, getMiniOffset(), 0);\n        mHeaderViewOffsetHelper.setTopAndBottomOffset(offset);\n        mContentViewOffsetHelper.setTopAndBottomOffset(offset);\n        if (mContentView != null) {\n            ((IQMUIContinuousNestedBottomView) mContentView).restoreScrollInfo(bundle);\n        }\n    }\n\n    // NestedScrollingChild2\n\n    @Override\n    public boolean startNestedScroll(int axes, int type) {\n        return mChildHelper.startNestedScroll(axes, type);\n    }\n\n    @Override\n    public void stopNestedScroll(int type) {\n        mChildHelper.stopNestedScroll(type);\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent(int type) {\n        return mChildHelper.hasNestedScrollingParent(type);\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,\n                                        int dyUnconsumed, int[] offsetInWindow, int type) {\n        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                offsetInWindow, type);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,\n                                           int type) {\n        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);\n    }\n\n    // NestedScrollingChild\n\n    @Override\n    public void setNestedScrollingEnabled(boolean enabled) {\n        mChildHelper.setNestedScrollingEnabled(enabled);\n    }\n\n    @Override\n    public boolean isNestedScrollingEnabled() {\n        return mChildHelper.isNestedScrollingEnabled();\n    }\n\n    @Override\n    public boolean startNestedScroll(int axes) {\n        return startNestedScroll(axes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void stopNestedScroll() {\n        stopNestedScroll(ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent() {\n        return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,\n                                        int dyUnconsumed, int[] offsetInWindow) {\n        return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                offsetInWindow, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {\n        return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {\n        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);\n    }\n\n    @Override\n    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {\n        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);\n    }\n\n    // NestedScrollingParent2\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,\n                                       int type) {\n        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;\n    }\n\n    @Override\n    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,\n                                       int type) {\n        mParentHelper.onNestedScrollAccepted(child, target, axes, type);\n        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type);\n    }\n\n    @Override\n    public void onStopNestedScroll(@NonNull View target, int type) {\n        mParentHelper.onStopNestedScroll(target, type);\n        stopNestedScroll(type);\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,\n                               int dyUnconsumed, int type) {\n        int remain = offsetBy(dyUnconsumed);\n        dispatchNestedScroll(0, dyUnconsumed - remain, 0, remain, null,\n                type);\n    }\n\n    @Override\n    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,\n                                  int type) {\n        dispatchNestedPreScroll(dx, dy, consumed, null, type);\n        int unconsumed = dy - consumed[1];\n        if (unconsumed > 0) {\n            consumed[1] += unconsumed - offsetBy(unconsumed);\n        }\n    }\n\n    // NestedScrollingParent\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {\n        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {\n        onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onStopNestedScroll(View target) {\n        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,\n                               int dyUnconsumed) {\n        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {\n        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {\n        if (!consumed) {\n            mViewFlinger.fling((int) velocityY);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {\n        return dispatchNestedPreFling(velocityX, velocityY);\n    }\n\n    @Override\n    public int getNestedScrollAxes() {\n        return mParentHelper.getNestedScrollAxes();\n    }\n\n\n    private boolean isPointInHeaderBounds(int x, int y) {\n        QMUIViewHelper.getDescendantRect(this, mHeaderView, mTempRect);\n        return mTempRect.contains(x, y);\n    }\n\n    private void ensureVelocityTracker() {\n        if (velocityTracker == null) {\n            velocityTracker = VelocityTracker.obtain();\n        }\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (touchSlop < 0) {\n            touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();\n        }\n        final int action = ev.getAction();\n\n        if (action == MotionEvent.ACTION_MOVE && isBeingDragged) {\n            return true;\n        }\n\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN: {\n                mViewFlinger.stop();\n                isBeingDragged = false;\n                final int x = (int) ev.getX();\n                final int y = (int) ev.getY();\n                if (isPointInHeaderBounds(x, y)) {\n                    lastMotionY = y;\n                    this.activePointerId = ev.getPointerId(0);\n                    startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_DOWN: {\n                final int actionIndex = ev.getActionIndex();\n                return actionIndex != 0 &&\n                        !isPointInHeaderBounds((int) ev.getX(), (int) ev.getY())\n                        && isPointInHeaderBounds((int) ev.getX(actionIndex), (int) ev.getY(actionIndex));\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerId = this.activePointerId;\n                if (activePointerId == INVALID_POINTER) {\n                    // If we don't have a valid id, the touch down wasn't on content.\n                    break;\n                }\n                final int pointerIndex = ev.findPointerIndex(activePointerId);\n                if (pointerIndex == -1) {\n                    break;\n                }\n\n                final int y = (int) ev.getY(pointerIndex);\n                final int yDiff = Math.abs(y - lastMotionY);\n                if (yDiff > touchSlop) {\n                    isBeingDragged = true;\n                    lastMotionY = y;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP: {\n                isBeingDragged = false;\n                this.activePointerId = INVALID_POINTER;\n                stopNestedScroll(ViewCompat.TYPE_TOUCH);\n                break;\n            }\n        }\n\n        return isBeingDragged;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (touchSlop < 0) {\n            touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();\n        }\n\n        if (ev.getAction() == MotionEvent.ACTION_DOWN) {\n            mNestedOffsetY = 0;\n        }\n\n        final MotionEvent vtev = MotionEvent.obtain(ev);\n        vtev.offsetLocation(0, mNestedOffsetY);\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN: {\n                mViewFlinger.stop();\n                final int x = (int) ev.getX();\n                final int y = (int) ev.getY();\n\n                if (isPointInHeaderBounds(x, y)) {\n                    lastMotionY = y;\n                    activePointerId = ev.getPointerId(0);\n                    ensureVelocityTracker();\n                    startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);\n                } else {\n                    return false;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = ev.findPointerIndex(activePointerId);\n                if (activePointerIndex == -1) {\n                    return false;\n                }\n\n                final int y = (int) ev.getY(activePointerIndex);\n                int dy = lastMotionY - y;\n\n                if (!isBeingDragged && Math.abs(dy) > touchSlop) {\n                    isBeingDragged = true;\n                    if (dy > 0) {\n                        dy -= touchSlop;\n                    } else {\n                        dy += touchSlop;\n                    }\n                }\n\n                if (isBeingDragged) {\n                    lastMotionY = y;\n                    if (dy < 0 && ((IQMUIContinuousNestedBottomView) mContentView).getCurrentScroll() > 0) {\n                        // the content view can scroll up, prevent drag\n                        return true;\n                    }\n                    mScrollConsumed[0] = 0;\n                    mScrollConsumed[1] = 0;\n                    if (dispatchNestedPreScroll(0, dy, mScrollConsumed, mScrollOffset)) {\n                        dy -= mScrollConsumed[1];\n                        lastMotionY = y - mScrollOffset[1];\n                        vtev.offsetLocation(0, mScrollOffset[1]);\n                        mNestedOffsetY += mScrollOffset[1];\n                    }\n                    int unconsumed = offsetBy(dy);\n                    if (dispatchNestedScroll(0, dy - unconsumed, 0, unconsumed, mScrollOffset, ViewCompat.TYPE_TOUCH)) {\n                        lastMotionY = y - mScrollOffset[1];\n                        vtev.offsetLocation(0, mScrollOffset[1]);\n                        mNestedOffsetY += mScrollOffset[1];\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_UP:\n                if (velocityTracker != null) {\n                    velocityTracker.addMovement(vtev);\n                    velocityTracker.computeCurrentVelocity(1000);\n                    int yvel = -(int) (velocityTracker.getYVelocity(activePointerId) + 0.5f);\n                    mViewFlinger.fling(yvel);\n                }\n                // $FALLTHROUGH\n            case MotionEvent.ACTION_CANCEL: {\n                isBeingDragged = false;\n                activePointerId = INVALID_POINTER;\n                if (velocityTracker != null) {\n                    velocityTracker.recycle();\n                    velocityTracker = null;\n                }\n                stopNestedScroll(ViewCompat.TYPE_TOUCH);\n                break;\n            }\n        }\n\n        if (velocityTracker != null) {\n            velocityTracker.addMovement(vtev);\n        }\n\n        vtev.recycle();\n\n        return true;\n    }\n\n    class ViewFlinger implements Runnable {\n        private int mLastFlingY;\n        OverScroller mOverScroller;\n        Interpolator mInterpolator = QUNITIC_INTERPOLATOR;\n\n        // When set to true, postOnAnimation callbacks are delayed until the run method completes\n        private boolean mEatRunOnAnimationRequest = false;\n\n        // Tracks if postAnimationCallback should be re-attached when it is done\n        private boolean mReSchedulePostAnimationCallback = false;\n\n        ViewFlinger() {\n            mOverScroller = new OverScroller(getContext(), QUNITIC_INTERPOLATOR);\n        }\n\n        @Override\n        public void run() {\n            mReSchedulePostAnimationCallback = false;\n            mEatRunOnAnimationRequest = true;\n\n            // Keep a local reference so that if it is changed during onAnimation method, it won't\n            // cause unexpected behaviors\n            final OverScroller scroller = mOverScroller;\n            if (scroller.computeScrollOffset()) {\n                final int y = scroller.getCurrY();\n                int unconsumedY = y - mLastFlingY;\n                mLastFlingY = y;\n                IQMUIContinuousNestedBottomView bottomView = (IQMUIContinuousNestedBottomView) mContentView;\n                boolean canScroll = unconsumedY <= 0 || bottomView.getCurrentScroll() < bottomView.getScrollOffsetRange();\n                if(canScroll){\n                    if (!mChildHelper.hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {\n                        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);\n                    }\n                    consumeScroll(unconsumedY);\n                    postOnAnimation();\n                }else{\n                    stop();\n                }\n            }\n\n            mEatRunOnAnimationRequest = false;\n            if (mReSchedulePostAnimationCallback) {\n                internalPostOnAnimation();\n            } else {\n                stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);\n            }\n        }\n\n        void postOnAnimation() {\n            if (mEatRunOnAnimationRequest) {\n                mReSchedulePostAnimationCallback = true;\n            } else {\n                internalPostOnAnimation();\n            }\n        }\n\n        private void internalPostOnAnimation() {\n            removeCallbacks(this);\n            ViewCompat.postOnAnimation(QMUIContinuousNestedBottomDelegateLayout.this, this);\n\n        }\n\n        public void fling(int velocityY) {\n            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);\n            mLastFlingY = 0;\n            // Because you can't define a custom interpolator for flinging, we should make sure we\n            // reset ourselves back to the teh default interpolator in case a different call\n            // changed our interpolator.\n            if (mInterpolator != QUNITIC_INTERPOLATOR) {\n                mInterpolator = QUNITIC_INTERPOLATOR;\n                mOverScroller = new OverScroller(getContext(), QUNITIC_INTERPOLATOR);\n            }\n            mOverScroller.fling(0, 0, 0, velocityY,\n                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);\n            postOnAnimation();\n        }\n\n\n        public void stop() {\n            removeCallbacks(this);\n            mOverScroller.abortAnimation();\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedBottomRecyclerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n\npublic class QMUIContinuousNestedBottomRecyclerView extends RecyclerView\n        implements IQMUIContinuousNestedBottomView {\n\n    public static final String KEY_SCROLL_INFO_POSITION = \"@qmui_scroll_info_bottom_rv_pos\";\n    public static final String KEY_SCROLL_INFO_OFFSET = \"@qmui_scroll_info_bottom_rv_offset\";\n\n    private IQMUIContinuousNestedBottomView.OnScrollNotifier mOnScrollNotifier;\n    private final int[] mScrollConsumed = new int[2];\n\n    public QMUIContinuousNestedBottomRecyclerView(@NonNull Context context) {\n        super(context);\n        init();\n    }\n\n    public QMUIContinuousNestedBottomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public QMUIContinuousNestedBottomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    private void init() {\n        setVerticalScrollBarEnabled(false);\n        addOnScrollListener(new OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {\n                if (mOnScrollNotifier != null) {\n                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {\n                        mOnScrollNotifier.onScrollStateChange(recyclerView,\n                                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_IDLE);\n                    } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {\n                        mOnScrollNotifier.onScrollStateChange(recyclerView,\n                                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_SETTLING);\n                    } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {\n                        mOnScrollNotifier.onScrollStateChange(recyclerView,\n                                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_DRAGGING);\n                    }\n                }\n            }\n\n            @Override\n            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {\n                if (mOnScrollNotifier != null) {\n                    mOnScrollNotifier.notify(\n                            recyclerView.computeVerticalScrollOffset(),\n                            Math.max(0, recyclerView.computeVerticalScrollRange() - recyclerView.getHeight()));\n                }\n            }\n        });\n    }\n\n    @Override\n    public void consumeScroll(int yUnconsumed) {\n        if (yUnconsumed == Integer.MIN_VALUE) {\n            if(canScrollVertically(-1)){\n                scrollToPosition(0);\n            }\n        } else if (yUnconsumed == Integer.MAX_VALUE) {\n            if(canScrollVertically(1)) {\n                Adapter adapter = getAdapter();\n                if (adapter != null) {\n                    scrollToPosition(adapter.getItemCount() - 1);\n                }\n            }\n        } else {\n            boolean reStartNestedScroll = false;\n            if (!hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {\n                // the scrollBy use ViewCompat.TYPE_TOUCH to handle nested scroll...\n                reStartNestedScroll = true;\n                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);\n\n                // and scrollBy only call dispatchNestedScroll, not call dispatchNestedPreScroll\n                mScrollConsumed[0] = 0;\n                mScrollConsumed[1] = 0;\n                dispatchNestedPreScroll(0, yUnconsumed, mScrollConsumed, null, ViewCompat.TYPE_TOUCH);\n                yUnconsumed -= mScrollConsumed[1];\n            }\n            scrollBy(0, yUnconsumed);\n            if (reStartNestedScroll) {\n                stopNestedScroll(ViewCompat.TYPE_TOUCH);\n            }\n        }\n    }\n\n    @Override\n    public int getContentHeight() {\n        Adapter adapter = getAdapter();\n        if (adapter == null) {\n            return 0;\n        }\n        LayoutManager layoutManager = getLayoutManager();\n        if (layoutManager == null) {\n            return 0;\n        }\n        final int scrollRange = this.computeVerticalScrollRange();\n        if (scrollRange > getHeight()) {\n            return HEIGHT_IS_ENOUGH_TO_SCROLL;\n        }\n        return scrollRange;\n    }\n\n    @Override\n    public void injectScrollNotifier(OnScrollNotifier notifier) {\n        mOnScrollNotifier = notifier;\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        return computeVerticalScrollOffset();\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        return Math.max(0, computeVerticalScrollRange() - getHeight());\n    }\n\n    @Override\n    public void smoothScrollYBy(int dy, int duration) {\n        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);\n        smoothScrollBy(0, dy, null);\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        LayoutManager layoutManager = getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            LinearLayoutManager lm = (LinearLayoutManager) layoutManager;\n            int pos = lm.findFirstVisibleItemPosition();\n            View firstView = lm.findViewByPosition(pos);\n            int offset = firstView == null ? 0 : firstView.getTop();\n            bundle.putInt(KEY_SCROLL_INFO_POSITION, pos);\n            bundle.putInt(KEY_SCROLL_INFO_OFFSET, offset);\n        }\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n        LayoutManager layoutManager = getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            int pos = bundle.getInt(KEY_SCROLL_INFO_POSITION, 0);\n            int offset = bundle.getInt(KEY_SCROLL_INFO_OFFSET, 0);\n            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(pos, offset);\n            if(mOnScrollNotifier != null){\n                mOnScrollNotifier.notify(getCurrentScroll(), getScrollOffsetRange());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedScrollLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUIContinuousNestedScrollLayout extends CoordinatorLayout implements\n        QMUIContinuousNestedTopAreaBehavior.Callback, QMUIDraggableScrollBar.Callback {\n    public static final String KEY_SCROLL_INFO_OFFSET = \"@qmui_nested_scroll_layout_offset\";\n\n    private IQMUIContinuousNestedTopView mTopView;\n    private IQMUIContinuousNestedBottomView mBottomView;\n\n    private QMUIContinuousNestedTopAreaBehavior mTopAreaBehavior;\n    private QMUIContinuousNestedBottomAreaBehavior mBottomAreaBehavior;\n    private List<OnScrollListener> mOnScrollListeners = new ArrayList<>();\n    private Runnable mCheckLayoutAction = new Runnable() {\n        @Override\n        public void run() {\n            checkLayout();\n        }\n    };\n    private boolean mKeepBottomAreaStableWhenCheckLayout = false;\n    private QMUIDraggableScrollBar mDraggableScrollBar;\n    private boolean mEnableScrollBarFadeInOut = true;\n    private boolean mIsDraggableScrollBarEnabled = false;\n    private int mCurrentScrollState = IQMUIContinuousNestedScrollCommon.SCROLL_STATE_IDLE;\n    private boolean mIsDismissDownEvent = false;\n    private float mDismissDownY = 0;\n    private int mTouchSlap = -1;\n\n    public QMUIContinuousNestedScrollLayout(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QMUIContinuousNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIContinuousNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    private void ensureScrollBar() {\n        if (mDraggableScrollBar == null) {\n            mDraggableScrollBar = createScrollBar(getContext());\n            mDraggableScrollBar.setEnableFadeInAndOut(mEnableScrollBarFadeInOut);\n            mDraggableScrollBar.setCallback(this);\n            CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            lp.gravity = Gravity.RIGHT;\n            addView(mDraggableScrollBar, lp);\n        }\n    }\n\n    public void setDraggableScrollBarEnabled(boolean draggableScrollBarEnabled) {\n        if(mIsDraggableScrollBarEnabled != draggableScrollBarEnabled){\n            mIsDraggableScrollBarEnabled = draggableScrollBarEnabled;\n            if(mIsDraggableScrollBarEnabled && !mEnableScrollBarFadeInOut){\n                ensureScrollBar();\n                mDraggableScrollBar.setPercent(getCurrentScrollPercent());\n                mDraggableScrollBar.awakenScrollBar();\n            }\n            if(mDraggableScrollBar != null){\n                mDraggableScrollBar.setVisibility(draggableScrollBarEnabled ? View.VISIBLE: View.GONE);\n            }\n        }\n    }\n\n    public void setEnableScrollBarFadeInOut(boolean enableScrollBarFadeInOut) {\n        if(mEnableScrollBarFadeInOut != enableScrollBarFadeInOut){\n            mEnableScrollBarFadeInOut = enableScrollBarFadeInOut;\n            if(mIsDraggableScrollBarEnabled && !mEnableScrollBarFadeInOut){\n                ensureScrollBar();\n                mDraggableScrollBar.setPercent(getCurrentScrollPercent());\n                mDraggableScrollBar.awakenScrollBar();\n            }\n            if(mDraggableScrollBar != null){\n                mDraggableScrollBar.setEnableFadeInAndOut(enableScrollBarFadeInOut);\n                mDraggableScrollBar.invalidate();\n            }\n        }\n    }\n\n    protected QMUIDraggableScrollBar createScrollBar(Context context) {\n        return new QMUIDraggableScrollBar(context);\n    }\n\n    @Override\n    public void onDragStarted() {\n        stopScroll();\n    }\n\n    @Override\n    public void onDragToPercent(float percent) {\n        int targetScroll = (int) (getScrollRange() * percent);\n        scrollBy(targetScroll - getCurrentScroll());\n    }\n\n    @Override\n    public void onDragEnd() {\n\n    }\n\n    public int getCurrentScroll() {\n        int currentScroll = 0;\n        if (mTopView != null) {\n            currentScroll += mTopView.getCurrentScroll();\n        }\n        currentScroll += getOffsetCurrent();\n        if (mBottomView != null) {\n            currentScroll += mBottomView.getCurrentScroll();\n        }\n        return currentScroll;\n    }\n\n    public int getScrollRange() {\n        int totalRange = 0;\n        if (mTopView != null) {\n            totalRange += mTopView.getScrollOffsetRange();\n        }\n        totalRange += getOffsetRange();\n\n        if (mBottomView != null) {\n            totalRange += mBottomView.getScrollOffsetRange();\n        }\n        return totalRange;\n    }\n\n    public float getCurrentScrollPercent() {\n        int scrollRange = getScrollRange();\n        if (scrollRange == 0) {\n            return 0;\n        }\n        return getCurrentScroll() * 1f / scrollRange;\n    }\n\n\n    public void addOnScrollListener(@NonNull OnScrollListener onScrollListener) {\n        if (!mOnScrollListeners.contains(onScrollListener)) {\n            mOnScrollListeners.add(onScrollListener);\n        }\n    }\n\n    public void removeOnScrollListener(OnScrollListener onScrollListener) {\n        mOnScrollListeners.remove(onScrollListener);\n    }\n\n    public void setKeepBottomAreaStableWhenCheckLayout(boolean keepBottomAreaStableWhenCheckLayout) {\n        mKeepBottomAreaStableWhenCheckLayout = keepBottomAreaStableWhenCheckLayout;\n    }\n\n    public boolean isKeepBottomAreaStableWhenCheckLayout() {\n        return mKeepBottomAreaStableWhenCheckLayout;\n    }\n\n    public void setTopAreaView(View topView, @Nullable LayoutParams layoutParams) {\n        if (!(topView instanceof IQMUIContinuousNestedTopView)) {\n            throw new IllegalStateException(\"topView must implement from IQMUIContinuousNestedTopView\");\n        }\n        if (mTopView != null) {\n            removeView(((View) mTopView));\n        }\n        mTopView = (IQMUIContinuousNestedTopView) topView;\n        mTopView.injectScrollNotifier(new IQMUIContinuousNestedScrollCommon.OnScrollNotifier() {\n            @Override\n            public void notify(int innerOffset, int innerRange) {\n                int offsetCurrent = mTopAreaBehavior == null ? 0 : -mTopAreaBehavior.getTopAndBottomOffset();\n                int bottomCurrent = mBottomView == null ? 0 : mBottomView.getCurrentScroll();\n                int bottomRange = mBottomView == null ? 0 : mBottomView.getScrollOffsetRange();\n                dispatchScroll(innerOffset, innerRange, offsetCurrent, getOffsetRange(), bottomCurrent, bottomRange);\n            }\n\n            @Override\n            public void onScrollStateChange(View view, int newScrollState) {\n                // not need this. top view scroll is driven by top behavior\n            }\n        });\n        if (layoutParams == null) {\n            layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        }\n\n        Behavior behavior = layoutParams.getBehavior();\n        if (behavior instanceof QMUIContinuousNestedTopAreaBehavior) {\n            mTopAreaBehavior = (QMUIContinuousNestedTopAreaBehavior) behavior;\n        } else {\n            mTopAreaBehavior = new QMUIContinuousNestedTopAreaBehavior(getContext());\n            layoutParams.setBehavior(mTopAreaBehavior);\n        }\n        mTopAreaBehavior.setCallback(this);\n        addView(topView, 0, layoutParams);\n    }\n\n    public IQMUIContinuousNestedTopView getTopView() {\n        return mTopView;\n    }\n\n    public IQMUIContinuousNestedBottomView getBottomView() {\n        return mBottomView;\n    }\n\n    public QMUIContinuousNestedTopAreaBehavior getTopAreaBehavior() {\n        return mTopAreaBehavior;\n    }\n\n    public QMUIContinuousNestedBottomAreaBehavior getBottomAreaBehavior() {\n        return mBottomAreaBehavior;\n    }\n\n    public void setBottomAreaView(View bottomView, @Nullable LayoutParams layoutParams) {\n        if (!(bottomView instanceof IQMUIContinuousNestedBottomView)) {\n            throw new IllegalStateException(\"bottomView must implement from IQMUIContinuousNestedBottomView\");\n        }\n        if (mBottomView != null) {\n            removeView(((View) mBottomView));\n        }\n        mBottomView = (IQMUIContinuousNestedBottomView) bottomView;\n        mBottomView.injectScrollNotifier(new IQMUIContinuousNestedBottomView.OnScrollNotifier() {\n            @Override\n            public void notify(int innerOffset, int innerRange) {\n                int topCurrent = mTopView == null ? 0 : mTopView.getCurrentScroll();\n                int topRange = mTopView == null ? 0 : mTopView.getScrollOffsetRange();\n                int offsetCurrent = mTopAreaBehavior == null ? 0 : -mTopAreaBehavior.getTopAndBottomOffset();\n                dispatchScroll(topCurrent, topRange, offsetCurrent, getOffsetRange(), innerOffset, innerRange);\n            }\n\n            @Override\n            public void onScrollStateChange(View view, int newScrollState) {\n                dispatchScrollStateChange(newScrollState, false);\n            }\n        });\n        if (layoutParams == null) {\n            layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n        }\n\n        Behavior behavior = layoutParams.getBehavior();\n        if (behavior instanceof QMUIContinuousNestedBottomAreaBehavior) {\n            mBottomAreaBehavior = (QMUIContinuousNestedBottomAreaBehavior) behavior;\n        } else {\n            mBottomAreaBehavior = new QMUIContinuousNestedBottomAreaBehavior();\n            layoutParams.setBehavior(mBottomAreaBehavior);\n        }\n        addView(bottomView, 0, layoutParams);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        super.onLayout(changed, l, t, r, b);\n        postCheckLayout();\n    }\n\n    public void postCheckLayout() {\n        removeCallbacks(mCheckLayoutAction);\n        post(mCheckLayoutAction);\n    }\n\n    public void checkLayout() {\n        if (mTopView == null || mBottomView == null) {\n            return;\n        }\n        int topCurrent = mTopView.getCurrentScroll();\n        int topRange = mTopView.getScrollOffsetRange();\n        int offsetCurrent = -mTopAreaBehavior.getTopAndBottomOffset();\n        int offsetRange = getOffsetRange();\n\n        if (offsetRange <= 0) {\n            return;\n        }\n\n        if (offsetCurrent >= offsetRange || (offsetCurrent > 0 && mKeepBottomAreaStableWhenCheckLayout)) {\n            mTopView.consumeScroll(Integer.MAX_VALUE);\n            if(mBottomView.getCurrentScroll() > 0){\n                mTopAreaBehavior.setTopAndBottomOffset(-offsetRange);\n            }\n            return;\n        }\n\n        if (mBottomView.getCurrentScroll() > 0) {\n            mBottomView.consumeScroll(Integer.MIN_VALUE);\n        }\n\n        if (topCurrent < topRange && offsetCurrent > 0) {\n            int remain = topRange - topCurrent;\n            if (offsetCurrent >= remain) {\n                mTopView.consumeScroll(Integer.MAX_VALUE);\n                mTopAreaBehavior.setTopAndBottomOffset(remain - offsetCurrent);\n            } else {\n                mTopView.consumeScroll(offsetCurrent);\n                mTopAreaBehavior.setTopAndBottomOffset(0);\n            }\n        }\n    }\n\n    public void scrollBottomViewToTop() {\n        if (mTopView != null) {\n            mTopView.consumeScroll(Integer.MAX_VALUE);\n        }\n\n        if (mBottomView != null) {\n            mBottomView.consumeScroll(Integer.MIN_VALUE);\n\n            int contentHeight = mBottomView.getContentHeight();\n            if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n                mTopAreaBehavior.setTopAndBottomOffset(Math.min(0, getHeight() - contentHeight - ((View) mTopView).getHeight()));\n            } else {\n                mTopAreaBehavior.setTopAndBottomOffset(\n                        getHeight() - ((View) mBottomView).getHeight() - ((View) mTopView).getHeight());\n            }\n        }\n    }\n\n    private void dispatchScroll(int topCurrent, int topRange,\n                                int offsetCurrent, int offsetRange,\n                                int bottomCurrent, int bottomRange) {\n        if (mIsDraggableScrollBarEnabled) {\n            ensureScrollBar();\n            mDraggableScrollBar.setPercent(getCurrentScrollPercent());\n            mDraggableScrollBar.awakenScrollBar();\n\n        }\n        for (OnScrollListener onScrollListener : mOnScrollListeners) {\n            onScrollListener.onScroll(this, topCurrent, topRange, offsetCurrent, offsetRange,\n                    bottomCurrent, bottomRange);\n        }\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {\n        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);\n        if(dyUnconsumed > 0 && getCurrentScroll() >= getScrollRange()){\n            // RecyclerView does not stop scroller when over scroll with NestedScrollingParent\n            stopScroll();\n        }\n    }\n\n    private void dispatchScrollStateChange(int newScrollState, boolean fromTopBehavior) {\n        for (OnScrollListener onScrollListener : mOnScrollListeners) {\n            onScrollListener.onScrollStateChange(this, newScrollState, fromTopBehavior);\n        }\n        mCurrentScrollState = newScrollState;\n    }\n\n    public void scrollBy(int dy) {\n        if ((dy > 0 || mBottomView == null) && mTopAreaBehavior != null) {\n            mTopAreaBehavior.scroll(this, ((View) mTopView), dy);\n        } else if (dy != 0 && mBottomView != null) {\n            mBottomView.consumeScroll(dy);\n        }\n    }\n\n    public void smoothScrollBy(int dy, int duration) {\n        if (dy == 0) {\n            return;\n        }\n        if ((dy > 0 || mBottomView == null) && mTopAreaBehavior != null) {\n            mTopAreaBehavior.smoothScrollBy(this, ((View) mTopView), dy, duration);\n        } else if (mBottomView != null) {\n            mBottomView.smoothScrollYBy(dy, duration);\n        }\n    }\n\n    public void stopScroll() {\n        if (mBottomView != null) {\n            mBottomView.stopScroll();\n        }\n        if (mTopAreaBehavior != null) {\n            mTopAreaBehavior.stopFlingOrScroll();\n        }\n    }\n\n    public void scrollToTop() {\n        if (mBottomView != null) {\n            mBottomView.consumeScroll(Integer.MIN_VALUE);\n        }\n        if (mTopView != null) {\n            mTopAreaBehavior.setTopAndBottomOffset(0);\n            mTopView.consumeScroll(Integer.MIN_VALUE);\n        }\n    }\n\n\n    public void scrollToBottom() {\n        if (mTopView != null) {\n            // consume the max value\n            mTopView.consumeScroll(Integer.MAX_VALUE);\n            if (mBottomView != null) {\n                int contentHeight = mBottomView.getContentHeight();\n                if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n                    // bottomView can not scroll\n                    View topView = (View) mTopView;\n                    if (topView.getHeight() + contentHeight < getHeight()) {\n                        mTopAreaBehavior.setTopAndBottomOffset(0);\n                    } else {\n                        mTopAreaBehavior.setTopAndBottomOffset(\n                                getHeight() - contentHeight - ((View) mTopView).getHeight());\n                    }\n                } else {\n                    mTopAreaBehavior.setTopAndBottomOffset(\n                            getHeight() - ((View) mBottomView).getHeight() - ((View) mTopView).getHeight());\n                }\n            }\n        }\n        if (mBottomView != null) {\n            mBottomView.consumeScroll(Integer.MAX_VALUE);\n        }\n    }\n\n    public int getOffsetCurrent() {\n        return mTopAreaBehavior == null ? 0 : -mTopAreaBehavior.getTopAndBottomOffset();\n    }\n\n    public int getOffsetRange() {\n        if (mTopView == null && mBottomView == null) {\n            return 0;\n        }\n        if(mBottomView == null){\n            return Math.max(0, ((View) mTopView).getHeight() - getHeight());\n        }\n        if(mTopView == null){\n            return 0;\n        }\n        int contentHeight = mBottomView.getContentHeight();\n        if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n            return Math.max(0, ((View) mTopView).getHeight() + contentHeight - getHeight());\n        }\n        return Math.max(0, ((View) mTopView).getHeight() + ((View) mBottomView).getHeight() - getHeight());\n    }\n\n    @Override\n    public void onTopAreaOffset(int offset) {\n        int topCurrent = mTopView == null ? 0 : mTopView.getCurrentScroll();\n        int topRange = mTopView == null ? 0 : mTopView.getScrollOffsetRange();\n        int bottomCurrent = mBottomView == null ? 0 : mBottomView.getCurrentScroll();\n        int bottomRange = mBottomView == null ? 0 : mBottomView.getScrollOffsetRange();\n        dispatchScroll(topCurrent, topRange, -offset, getOffsetRange(), bottomCurrent, bottomRange);\n    }\n\n    @Override\n    public void onTopBehaviorTouchBegin() {\n        dispatchScrollStateChange(\n                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_DRAGGING, true);\n    }\n\n    @Override\n    public void onTopBehaviorTouchEnd() {\n        dispatchScrollStateChange(\n                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_IDLE, true);\n    }\n\n    @Override\n    public void onTopBehaviorFlingOrScrollStart() {\n        dispatchScrollStateChange(\n                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_SETTLING, true);\n    }\n\n    @Override\n    public void onTopBehaviorFlingOrScrollEnd() {\n        dispatchScrollStateChange(\n                IQMUIContinuousNestedScrollCommon.SCROLL_STATE_IDLE, true);\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        if (ev.getAction() == MotionEvent.ACTION_DOWN) {\n            if(mCurrentScrollState != IQMUIContinuousNestedScrollCommon.SCROLL_STATE_IDLE){\n                // must stop scroll and not use the current down event.\n                // this is worked when topView scroll to bottomView or bottomView scroll to topView.\n                stopScroll();\n                mIsDismissDownEvent = true;\n                mDismissDownY = ev.getY();\n                if(mTouchSlap < 0){\n                    mTouchSlap = ViewConfiguration.get(getContext()).getScaledTouchSlop();\n                }\n                return true;\n            }\n        } else if(ev.getAction() == MotionEvent.ACTION_MOVE && mIsDismissDownEvent){\n            if(Math.abs(ev.getY() - mDismissDownY) > mTouchSlap){\n                MotionEvent down = MotionEvent.obtain(ev);\n                down.setAction(MotionEvent.ACTION_DOWN);\n                down.offsetLocation(0, mDismissDownY - ev.getY());\n                super.dispatchTouchEvent(down);\n                down.recycle();\n            }else{\n                return true;\n            }\n        }\n        mIsDismissDownEvent = false;\n        return super.dispatchTouchEvent(ev);\n    }\n\n    /**\n     * save current scroll info to bundle\n     *\n     * @param bundle\n     */\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        if (mTopView != null) {\n            mTopView.saveScrollInfo(bundle);\n        }\n        if (mBottomView != null) {\n            mBottomView.saveScrollInfo(bundle);\n        }\n        bundle.putInt(KEY_SCROLL_INFO_OFFSET, getOffsetCurrent());\n    }\n\n\n    /**\n     * restore current scroll info from bundle\n     *\n     * @param bundle\n     */\n    public void restoreScrollInfo(@Nullable Bundle bundle) {\n        if (bundle == null) {\n            return;\n        }\n        if (mTopAreaBehavior != null) {\n            int offset = bundle.getInt(KEY_SCROLL_INFO_OFFSET, 0);\n            mTopAreaBehavior.setTopAndBottomOffset(QMUILangHelper.constrain(-offset, -getOffsetRange(), 0));\n        }\n        if (mTopView != null) {\n            mTopView.restoreScrollInfo(bundle);\n        }\n\n        if (mBottomView != null) {\n            mBottomView.restoreScrollInfo(bundle);\n        }\n    }\n\n    public interface OnScrollListener {\n\n        void onScroll(QMUIContinuousNestedScrollLayout scrollLayout, int topCurrent, int topRange,\n                      int offsetCurrent, int offsetRange,\n                      int bottomCurrent, int bottomRange);\n\n        void onScrollStateChange(QMUIContinuousNestedScrollLayout scrollLayout, int newScrollState, boolean fromTopBehavior);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedTopAreaBehavior.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.animation.Interpolator;\nimport android.webkit.WebView;\nimport android.widget.OverScroller;\n\nimport androidx.annotation.NonNull;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.ViewCompat;\nimport static android.view.View.MEASURED_SIZE_MASK;\nimport static com.qmuiteam.qmui.QMUIInterpolatorStaticHolder.QUNITIC_INTERPOLATOR;\n\npublic class QMUIContinuousNestedTopAreaBehavior extends QMUIViewOffsetBehavior<View> {\n\n    private static final int INVALID_POINTER = -1;\n\n    private final ViewFlinger mViewFlinger;\n    private final int[] mScrollConsumed = new int[2];\n\n    private boolean isBeingDragged;\n    private int activePointerId = INVALID_POINTER;\n    private int lastMotionY;\n    private int touchSlop = -1;\n    private VelocityTracker velocityTracker;\n    private Callback mCallback;\n    private boolean isInTouch = false;\n    private boolean isInFlingOrScroll = false;\n    private boolean replaceCancelActionWithMoveActionForWebView = true;\n\n    public QMUIContinuousNestedTopAreaBehavior(Context context) {\n        this(context, null);\n    }\n\n\n    public QMUIContinuousNestedTopAreaBehavior(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        mViewFlinger = new ViewFlinger(context);\n    }\n\n    public void setReplaceCancelActionWithMoveActionForWebView(boolean replaceCancelActionWithMoveActionForWebView) {\n        this.replaceCancelActionWithMoveActionForWebView = replaceCancelActionWithMoveActionForWebView;\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent,\n                                         @NonNull View child, @NonNull MotionEvent ev) {\n        if (touchSlop < 0) {\n            touchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();\n        }\n        final int action = ev.getAction();\n\n        if (action == MotionEvent.ACTION_MOVE && isBeingDragged) {\n            return true;\n        }\n\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN: {\n                mViewFlinger.stop();\n                isInTouch = true;\n                isBeingDragged = false;\n                final int x = (int) ev.getX();\n                final int y = (int) ev.getY();\n                if (parent.isPointInChildBounds(child, x, y)) {\n                    lastMotionY = y;\n                    this.activePointerId = ev.getPointerId(0);\n                    ensureVelocityTracker();\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_DOWN: {\n                final int actionIndex = ev.getActionIndex();\n                return actionIndex != 0 &&\n                        !parent.isPointInChildBounds(child, (int) ev.getX(), (int) ev.getY())\n                        && parent.isPointInChildBounds(\n                        child, (int) ev.getX(actionIndex), (int) ev.getY(actionIndex));\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerId = this.activePointerId;\n                if (activePointerId == INVALID_POINTER) {\n                    // If we don't have a valid id, the touch down wasn't on content.\n                    break;\n                }\n                final int pointerIndex = ev.findPointerIndex(activePointerId);\n                if (pointerIndex == -1) {\n                    break;\n                }\n\n                final int y = (int) ev.getY(pointerIndex);\n                final int yDiff = Math.abs(y - lastMotionY);\n                if (yDiff > touchSlop) {\n                    isBeingDragged = true;\n                    if(child instanceof WebView || child instanceof QMUIContinuousNestedTopDelegateLayout){\n                        // dispatch cancel event not work in webView sometimes.\n                        MotionEvent cancelEvent = MotionEvent.obtain(ev);\n                        cancelEvent.offsetLocation(-child.getLeft(), -child.getTop());\n                        if(replaceCancelActionWithMoveActionForWebView){\n                            cancelEvent.setAction(MotionEvent.ACTION_MOVE);\n                        }else{\n                            cancelEvent.setAction(MotionEvent.ACTION_CANCEL);\n                        }\n                        child.dispatchTouchEvent(cancelEvent);\n                        cancelEvent.recycle();\n                    }\n                    lastMotionY = y;\n                    if (mCallback != null) {\n                        mCallback.onTopBehaviorTouchBegin();\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP: {\n                isInTouch = false;\n                isBeingDragged = false;\n                this.activePointerId = INVALID_POINTER;\n                if (velocityTracker != null) {\n                    velocityTracker.recycle();\n                    velocityTracker = null;\n                }\n                break;\n            }\n        }\n\n        if (velocityTracker != null) {\n            velocityTracker.addMovement(ev);\n        }\n\n        return isBeingDragged;\n    }\n\n    @Override\n    public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull MotionEvent ev) {\n        if (touchSlop < 0) {\n            touchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN: {\n                mViewFlinger.stop();\n                isInTouch = true;\n                final int x = (int) ev.getX();\n                final int y = (int) ev.getY();\n\n                if (parent.isPointInChildBounds(child, x, y)) {\n                    lastMotionY = y;\n                    activePointerId = ev.getPointerId(0);\n                    ensureVelocityTracker();\n                } else {\n                    return false;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = ev.findPointerIndex(activePointerId);\n                if (activePointerIndex == -1) {\n                    return false;\n                }\n\n                final int y = (int) ev.getY(activePointerIndex);\n                int dy = lastMotionY - y;\n\n                if (!isBeingDragged && Math.abs(dy) > touchSlop) {\n                    isBeingDragged = true;\n                    if (mCallback != null) {\n                        mCallback.onTopBehaviorTouchBegin();\n                    }\n                    if (dy > 0) {\n                        dy -= touchSlop;\n                    } else {\n                        dy += touchSlop;\n                    }\n                }\n\n                if (isBeingDragged) {\n                    lastMotionY = y;\n                    scroll(parent, child, dy);\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_UP:\n                isInTouch = false;\n                if (mCallback != null) {\n                    mCallback.onTopBehaviorTouchEnd();\n                }\n                if (velocityTracker != null) {\n                    velocityTracker.addMovement(ev);\n                    velocityTracker.computeCurrentVelocity(1000);\n                    int yvel = -(int) (velocityTracker.getYVelocity(activePointerId) + 0.5f);\n                    mViewFlinger.fling(parent, child, yvel);\n                }\n                // $FALLTHROUGH\n            case MotionEvent.ACTION_CANCEL: {\n                if (isInTouch) {\n                    isInTouch = false;\n                    if (mCallback != null) {\n                        mCallback.onTopBehaviorTouchEnd();\n                    }\n                }\n                isBeingDragged = false;\n                activePointerId = INVALID_POINTER;\n                if (velocityTracker != null) {\n                    velocityTracker.recycle();\n                    velocityTracker = null;\n                }\n                break;\n            }\n        }\n\n        if (velocityTracker != null) {\n            velocityTracker.addMovement(ev);\n        }\n\n        return true;\n    }\n\n    void scroll(@NonNull CoordinatorLayout parent, @NonNull View child, int dy) {\n        mScrollConsumed[0] = 0;\n        mScrollConsumed[1] = 0;\n        onNestedPreScroll(parent, child, child, 0, dy, mScrollConsumed, ViewCompat.TYPE_TOUCH);\n        int unConsumed = dy - mScrollConsumed[1];\n        if (child instanceof IQMUIContinuousNestedTopView) {\n            unConsumed = ((IQMUIContinuousNestedTopView) child).consumeScroll(unConsumed);\n        }\n        onNestedScroll(parent, child, child, 0, dy - unConsumed,\n                0, unConsumed, ViewCompat.TYPE_TOUCH);\n\n    }\n\n    void smoothScrollBy(@NonNull CoordinatorLayout parent, @NonNull View child, int dy, int duration) {\n        mViewFlinger.startScroll(parent, child, dy, duration);\n    }\n\n    void stopFlingOrScroll() {\n        mViewFlinger.stop();\n    }\n\n    private void ensureVelocityTracker() {\n        if (velocityTracker == null) {\n            velocityTracker = VelocityTracker.obtain();\n        }\n    }\n\n    @Override\n    public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull View child,\n                                  int parentWidthMeasureSpec, int widthUsed,\n                                  int parentHeightMeasureSpec, int heightUsed) {\n        final int childLpHeight = child.getLayoutParams().height;\n        int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);\n        if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT) {\n            if (availableHeight == 0) {\n                // If the measure spec doesn't specify a size, use the current height\n                availableHeight = parent.getHeight();\n            }\n            final int heightMeasureSpec =\n                    View.MeasureSpec.makeMeasureSpec(availableHeight, View.MeasureSpec.AT_MOST);\n\n            parent.onMeasureChild(\n                    child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);\n\n\n        } else {\n            parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed,\n                    View.MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, View.MeasureSpec.AT_MOST), heightUsed);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {\n        boolean ret =  super.onLayoutChild(parent, child, layoutDirection);\n        int top = child.getTop();\n        int layoutTop = getLayoutTop();\n        if(top > layoutTop){\n            setTopAndBottomOffset(0);\n        }else if(child.getBottom() < layoutTop + child.getHeight()){\n            setTopAndBottomOffset(-child.getHeight());\n        }\n        return ret;\n    }\n\n    @Override\n    public void onNestedPreScroll(@NonNull CoordinatorLayout parent, @NonNull View child,\n                                  @NonNull View target, int dx, int dy,\n                                  @NonNull int[] consumed, int type) {\n        if (target.getParent() != parent) {\n            return;\n        }\n        if (target == child) {\n            // both target view and child view is top view\n            if (dy < 0) {\n                if (child.getTop() <= dy) {\n                    setTopAndBottomOffset(child.getTop() - dy - getLayoutTop());\n                    consumed[1] += dy;\n                } else if (child.getTop() < 0) {\n                    int top = child.getTop();\n                    setTopAndBottomOffset(0 - getLayoutTop());\n                    consumed[1] += top;\n                }\n            }\n        } else {\n            if (dy > 0) {\n                // child is topView, target is bottomView\n                if (target instanceof IQMUIContinuousNestedBottomView) {\n                    int contentHeight = ((IQMUIContinuousNestedBottomView) target).getContentHeight();\n                    int minOffset;\n                    if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n                        minOffset = parent.getHeight() - contentHeight - child.getHeight();\n                    } else {\n                        minOffset = parent.getHeight() - child.getHeight() - target.getHeight();\n                    }\n                    if (child.getTop() - dy >= minOffset) {\n                        setTopAndBottomOffset(child.getTop() - dy - getLayoutTop());\n                        consumed[1] += dy;\n                    } else if (child.getTop() > minOffset) {\n                        int distance = child.getTop() - minOffset;\n                        setTopAndBottomOffset(minOffset);\n                        consumed[1] += distance;\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public void onNestedScroll(@NonNull CoordinatorLayout parent, @NonNull View child,\n                               @NonNull View target, int dxConsumed, int dyConsumed,\n                               int dxUnconsumed, int dyUnconsumed, int type) {\n        if (target.getParent() != parent) {\n            return;\n        }\n        if (target == child) {\n            // both target view and child view is top view\n            if (dyUnconsumed > 0) {\n                View bottomView = findBottomView(parent);\n                if (bottomView == null || bottomView.getVisibility() == View.GONE) {\n                    int parentBottom = parent.getHeight();\n                    if (target.getBottom() - parentBottom >= dyUnconsumed) {\n                        setTopAndBottomOffset(target.getTop() - dyUnconsumed - getLayoutTop());\n                    } else if (target.getBottom() - parentBottom > 0) {\n                        int moveDistance = target.getBottom() - parentBottom;\n                        setTopAndBottomOffset(target.getTop() - moveDistance - getLayoutTop());\n                    }\n                } else {\n                    int contentHeight = ((IQMUIContinuousNestedBottomView) bottomView).getContentHeight();\n                    int minBottom = parent.getHeight();\n                    boolean canContentScroll = true;\n                    if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {\n                        minBottom = parent.getHeight() + bottomView.getHeight() - contentHeight;\n                        canContentScroll = false;\n                    }\n                    if (bottomView.getBottom() - minBottom > dyUnconsumed) {\n                        setTopAndBottomOffset(target.getTop() - dyUnconsumed - getLayoutTop());\n                        return;\n                    } else if (bottomView.getBottom() - minBottom > 0) {\n                        int moveDistance = bottomView.getBottom() - minBottom;\n                        setTopAndBottomOffset(target.getTop() - moveDistance - getLayoutTop());\n                        dyUnconsumed = dyUnconsumed == Integer.MAX_VALUE ? dyUnconsumed : (dyUnconsumed - moveDistance);\n                    }\n                    if (canContentScroll) {\n                        ((IQMUIContinuousNestedBottomView) bottomView).consumeScroll(dyUnconsumed);\n                    }\n                }\n            }\n        } else {\n            // child is topView, target is bottomView\n            if (dyUnconsumed < 0) {\n                if (child.getTop() <= dyUnconsumed) {\n                    setTopAndBottomOffset(child.getTop() - dyUnconsumed - getLayoutTop());\n                    return;\n                } else if (child.getTop() < 0) {\n                    int top = child.getTop();\n                    setTopAndBottomOffset(0 - getLayoutTop());\n                    dyUnconsumed = dyUnconsumed == Integer.MIN_VALUE ? dyConsumed : (dyUnconsumed - top);\n                }\n                if (child instanceof IQMUIContinuousNestedTopView) {\n                    ((IQMUIContinuousNestedTopView) child).consumeScroll(dyUnconsumed);\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,\n                                       @NonNull View child, @NonNull View directTargetChild,\n                                       @NonNull View target, int axes, int type) {\n        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;\n    }\n\n    private View findBottomView(CoordinatorLayout parent) {\n        for (int i = 0; i < parent.getChildCount(); i++) {\n            View child = parent.getChildAt(i);\n            if (child instanceof IQMUIContinuousNestedBottomView) {\n                return child;\n            }\n        }\n        return null;\n    }\n\n\n    class ViewFlinger implements Runnable {\n        private int mLastFlingY;\n        OverScroller mOverScroller;\n        Interpolator mInterpolator = QUNITIC_INTERPOLATOR;\n\n        // When set to true, postOnAnimation callbacks are delayed until the run method completes\n        private boolean mEatRunOnAnimationRequest = false;\n\n        // Tracks if postAnimationCallback should be re-attached when it is done\n        private boolean mReSchedulePostAnimationCallback = false;\n\n        private CoordinatorLayout mCurrentParent;\n        private View mCurrentChild;\n\n        ViewFlinger(Context context) {\n            mOverScroller = new OverScroller(context, QUNITIC_INTERPOLATOR);\n        }\n\n        @Override\n        public void run() {\n            mReSchedulePostAnimationCallback = false;\n            mEatRunOnAnimationRequest = true;\n\n            // Keep a local reference so that if it is changed during onAnimation method, it won't\n            // cause unexpected behaviors\n            final OverScroller scroller = mOverScroller;\n            if (scroller.computeScrollOffset()) {\n                final int y = scroller.getCurrY();\n                int unconsumedY = y - mLastFlingY;\n                mLastFlingY = y;\n                if (mCurrentParent != null && mCurrentChild != null) {\n                    boolean canScroll = true;\n                    if(mCurrentParent instanceof QMUIContinuousNestedScrollLayout){\n                        QMUIContinuousNestedScrollLayout layout = (QMUIContinuousNestedScrollLayout) mCurrentParent;\n                        if(unconsumedY > 0 && layout.getCurrentScroll() >= layout.getScrollRange()){\n                            canScroll = false;\n                        }else if(unconsumedY < 0 && layout.getCurrentScroll() <= 0){\n                            canScroll = false;\n                        }\n                    }\n                    if(canScroll){\n                        scroll(mCurrentParent, mCurrentChild, unconsumedY);\n                        postOnAnimation();\n                    }else{\n                        mOverScroller.abortAnimation();\n                    }\n                }\n            }\n\n            mEatRunOnAnimationRequest = false;\n            if (mReSchedulePostAnimationCallback) {\n                internalPostOnAnimation();\n            } else {\n                mCurrentParent = null;\n                mCurrentChild = null;\n                onFlingOrScrollEnd();\n            }\n        }\n\n        void postOnAnimation() {\n            if (mEatRunOnAnimationRequest) {\n                mReSchedulePostAnimationCallback = true;\n            } else {\n                internalPostOnAnimation();\n            }\n        }\n\n        private void internalPostOnAnimation() {\n            if (mCurrentChild != null) {\n                mCurrentParent.removeCallbacks(this);\n                ViewCompat.postOnAnimation(mCurrentChild, this);\n            }\n\n        }\n\n        public void fling(CoordinatorLayout parent, View child, int velocityY) {\n            onFlingOrScrollStart(parent, child);\n            mOverScroller.fling(0, 0, 0, velocityY,\n                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);\n            postOnAnimation();\n        }\n\n        public void startScroll(CoordinatorLayout parent, View child, int dy, int duration) {\n            onFlingOrScrollStart(parent, child);\n            mOverScroller.startScroll(0, 0, 0, dy, duration);\n            postOnAnimation();\n        }\n\n        private void onFlingOrScrollStart(CoordinatorLayout parent, View child) {\n            isInFlingOrScroll = true;\n            if (mCallback != null) {\n                mCallback.onTopBehaviorFlingOrScrollStart();\n            }\n            mCurrentParent = parent;\n            mCurrentChild = child;\n            mLastFlingY = 0;\n            // Because you can't define a custom interpolator for flinging, we should make sure we\n            // reset ourselves back to the teh default interpolator in case a different call\n            // changed our interpolator.\n            if (mInterpolator != QUNITIC_INTERPOLATOR) {\n                mInterpolator = QUNITIC_INTERPOLATOR;\n                mOverScroller = new OverScroller(mCurrentParent.getContext(), QUNITIC_INTERPOLATOR);\n            }\n        }\n\n\n        public void stop() {\n            if (mCurrentChild != null) {\n                mCurrentChild.removeCallbacks(this);\n            }\n            mOverScroller.abortAnimation();\n            mCurrentChild = null;\n            mCurrentParent = null;\n            onFlingOrScrollEnd();\n        }\n\n        private void onFlingOrScrollEnd() {\n            if (mCallback != null && isInFlingOrScroll) {\n                mCallback.onTopBehaviorFlingOrScrollEnd();\n            }\n            isInFlingOrScroll = false;\n        }\n    }\n\n    @Override\n    public boolean setTopAndBottomOffset(int offset) {\n        boolean ret = super.setTopAndBottomOffset(offset);\n        if (mCallback != null) {\n            mCallback.onTopAreaOffset(offset);\n        }\n        return ret;\n    }\n\n    public interface Callback {\n        void onTopAreaOffset(int offset);\n\n        void onTopBehaviorTouchBegin();\n\n        void onTopBehaviorTouchEnd();\n\n        void onTopBehaviorFlingOrScrollStart();\n\n        void onTopBehaviorFlingOrScrollEnd();\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedTopDelegateLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.NestedScrollingChild2;\nimport androidx.core.view.NestedScrollingChildHelper;\nimport androidx.core.view.NestedScrollingParent2;\nimport androidx.core.view.NestedScrollingParentHelper;\nimport androidx.core.view.ViewCompat;\n\npublic class QMUIContinuousNestedTopDelegateLayout extends FrameLayout implements\n        NestedScrollingChild2, NestedScrollingParent2, IQMUIContinuousNestedTopView {\n\n    public static final String KEY_SCROLL_INFO_OFFSET = \"@qmui_scroll_info_top_dl_offset\";\n\n    private OnScrollNotifier mScrollNotifier;\n    private View mHeaderView;\n    private IQMUIContinuousNestedTopView mDelegateView;\n    private View mFooterView;\n    private QMUIViewOffsetHelper mHeaderViewOffsetHelper;\n    private QMUIViewOffsetHelper mDelegateViewOffsetHelper;\n    private QMUIViewOffsetHelper mFooterViewOffsetHelper;\n    private int mOffsetCurrent = 0;\n    private int mOffsetRange = 0;\n    private final NestedScrollingParentHelper mParentHelper;\n    private final NestedScrollingChildHelper mChildHelper;\n    private Runnable mCheckLayoutAction = new Runnable() {\n        @Override\n        public void run() {\n            checkLayout();\n        }\n    };\n\n    public QMUIContinuousNestedTopDelegateLayout(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QMUIContinuousNestedTopDelegateLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIContinuousNestedTopDelegateLayout(@NonNull Context context,\n                                                 @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        mParentHelper = new NestedScrollingParentHelper(this);\n        mChildHelper = new NestedScrollingChildHelper(this);\n\n        ViewCompat.setNestedScrollingEnabled(this, true);\n        setClipToPadding(false);\n    }\n\n    public void setHeaderView(@NonNull View headerView) {\n        mHeaderView = headerView;\n        mHeaderViewOffsetHelper = new QMUIViewOffsetHelper(headerView);\n        addView(headerView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));\n    }\n\n    public void setDelegateView(@NonNull IQMUIContinuousNestedTopView delegateView) {\n        if (!(delegateView instanceof View)) {\n            throw new IllegalArgumentException(\"delegateView must be a instance of View\");\n        }\n        if (mDelegateView != null) {\n            mDelegateView.injectScrollNotifier(null);\n        }\n        mDelegateView = delegateView;\n        View view = (View) delegateView;\n        mDelegateViewOffsetHelper = new QMUIViewOffsetHelper(view);\n        // WRAP_CONTENT, the height will be handled by QMUIContinuousNestedTopAreaBehavior\n        addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n    }\n\n    public void setFooterView(@NonNull View footerView) {\n        mFooterView = footerView;\n        mFooterViewOffsetHelper = new QMUIViewOffsetHelper(footerView);\n        addView(footerView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int w = MeasureSpec.getSize(widthMeasureSpec);\n        int h = MeasureSpec.getSize(heightMeasureSpec);\n        int anchorHeight = getPaddingTop();\n        if (mHeaderView != null) {\n            mHeaderView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.UNSPECIFIED));\n            anchorHeight += mHeaderView.getMeasuredHeight();\n        }\n        if (mDelegateView != null) {\n            View delegateView = (View) mDelegateView;\n            delegateView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));\n            anchorHeight += delegateView.getMeasuredHeight();\n        }\n\n        if (mFooterView != null) {\n            mFooterView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.UNSPECIFIED));\n            anchorHeight += mFooterView.getMeasuredHeight();\n        }\n\n        anchorHeight += getPaddingBottom();\n        if (anchorHeight < h) {\n            setMeasuredDimension(w, anchorHeight);\n        } else {\n            setMeasuredDimension(w, h);\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        int w = right - left, h = bottom - top;\n        int anchorTop = getPaddingTop();\n        int viewHeight;\n        if (mHeaderView != null) {\n            viewHeight = mHeaderView.getMeasuredHeight();\n            mHeaderView.layout(0, anchorTop, w, anchorTop + viewHeight);\n            anchorTop += viewHeight;\n        }\n\n        if (mDelegateView != null) {\n            View view = (View) mDelegateView;\n            viewHeight = view.getMeasuredHeight();\n            view.layout(0, anchorTop, w, anchorTop + viewHeight);\n            anchorTop += viewHeight;\n        }\n\n        if (mFooterView != null) {\n            viewHeight = mFooterView.getMeasuredHeight();\n            mFooterView.layout(0, anchorTop, w, anchorTop + viewHeight);\n            anchorTop += viewHeight;\n        }\n        anchorTop += getPaddingBottom();\n\n        mOffsetRange = Math.max(0, anchorTop - h);\n\n        if (mHeaderViewOffsetHelper != null) {\n            mHeaderViewOffsetHelper.onViewLayout();\n            mOffsetCurrent = -mHeaderViewOffsetHelper.getTopAndBottomOffset();\n        }\n\n        if (mDelegateViewOffsetHelper != null) {\n            mDelegateViewOffsetHelper.onViewLayout();\n            mOffsetCurrent = -mDelegateViewOffsetHelper.getTopAndBottomOffset();\n        }\n\n        if (mFooterViewOffsetHelper != null) {\n            mFooterViewOffsetHelper.onViewLayout();\n            mOffsetCurrent = -mFooterViewOffsetHelper.getTopAndBottomOffset();\n        }\n\n        if(mOffsetCurrent > mOffsetRange){\n            offsetTo(mOffsetRange);\n        }\n        postCheckLayout();\n    }\n\n    public void postCheckLayout() {\n        removeCallbacks(mCheckLayoutAction);\n        post(mCheckLayoutAction);\n    }\n\n    public void checkLayout() {\n        if (mHeaderView == null && mFooterView == null) {\n            return;\n        }\n        if (mDelegateView == null) {\n            return;\n        }\n        int headerOffsetRange = getContainerHeaderOffsetRange();\n        int delegateCurrentScroll = mDelegateView.getCurrentScroll();\n        int delegateScrollRange = mDelegateView.getScrollOffsetRange();\n        if (delegateCurrentScroll > 0 && mHeaderView != null && mOffsetCurrent < headerOffsetRange) {\n            int over = headerOffsetRange - mOffsetCurrent;\n            if (over >= delegateCurrentScroll) {\n                mDelegateView.consumeScroll(Integer.MIN_VALUE);\n                offsetTo(mOffsetCurrent + delegateCurrentScroll);\n            } else {\n                mDelegateView.consumeScroll(-over);\n                offsetTo(headerOffsetRange);\n            }\n\n        }\n\n        if (mOffsetCurrent > headerOffsetRange && delegateCurrentScroll < delegateScrollRange\n                && mFooterView != null) {\n            int over = mOffsetCurrent - headerOffsetRange;\n            int delegateRemain = delegateScrollRange - delegateCurrentScroll;\n            if (over >= delegateRemain) {\n                mDelegateView.consumeScroll(Integer.MAX_VALUE);\n                offsetTo(headerOffsetRange + over - delegateRemain);\n            } else {\n                mDelegateView.consumeScroll(over);\n                offsetTo(headerOffsetRange);\n            }\n        }\n\n    }\n\n    private void offsetTo(int targetOffsetCurrent) {\n        mOffsetCurrent = targetOffsetCurrent;\n        if (mHeaderViewOffsetHelper != null) {\n            mHeaderViewOffsetHelper.setTopAndBottomOffset(-targetOffsetCurrent);\n        }\n\n        if (mDelegateViewOffsetHelper != null) {\n            mDelegateViewOffsetHelper.setTopAndBottomOffset(-targetOffsetCurrent);\n        }\n\n        if (mFooterViewOffsetHelper != null) {\n            mFooterViewOffsetHelper.setTopAndBottomOffset(-targetOffsetCurrent);\n        }\n        if (mScrollNotifier != null) {\n            mScrollNotifier.notify(getCurrentScroll(), getScrollOffsetRange());\n        }\n    }\n\n    public IQMUIContinuousNestedTopView getDelegateView() {\n        return mDelegateView;\n    }\n\n    public View getHeaderView() {\n        return mHeaderView;\n    }\n\n    public View getFooterView() {\n        return mFooterView;\n    }\n\n    public int getContainerOffsetCurrent() {\n        return mOffsetCurrent;\n    }\n\n    public int getContainerOffsetRange() {\n        return mOffsetRange;\n    }\n\n    public int getContainerHeaderOffsetRange() {\n        if (mOffsetRange == 0 || mHeaderView == null) {\n            return 0;\n        }\n        int maxHeight = getPaddingTop() + mHeaderView.getHeight();\n        return Math.min(maxHeight, mOffsetRange);\n    }\n\n    @Override\n    public int consumeScroll(int dyUnconsumed) {\n        if (mOffsetRange <= 0) {\n            if (mDelegateView != null) {\n                return mDelegateView.consumeScroll(dyUnconsumed);\n            }\n            return dyUnconsumed;\n        }\n\n        if (dyUnconsumed > 0) {\n            if (mDelegateView == null) {\n                if (dyUnconsumed == Integer.MAX_VALUE) {\n                    offsetTo(mOffsetRange);\n                } else if (mOffsetCurrent + dyUnconsumed <= mOffsetRange) {\n                    offsetTo(mOffsetCurrent + dyUnconsumed);\n                    return 0;\n                } else if (mOffsetCurrent < mOffsetRange) {\n                    dyUnconsumed -= mOffsetRange - mOffsetCurrent;\n                    offsetTo(mOffsetRange);\n\n                }\n                return dyUnconsumed;\n            } else {\n                int beforeRange = Math.min(mOffsetRange,\n                        getPaddingTop() + (mHeaderView == null ? 0 : mHeaderView.getHeight()));\n                if (dyUnconsumed == Integer.MAX_VALUE) {\n                    offsetTo(beforeRange);\n                } else if (mOffsetCurrent + dyUnconsumed <= beforeRange) {\n                    offsetTo(mOffsetCurrent + dyUnconsumed);\n                    return 0;\n                } else if (mOffsetCurrent < beforeRange) {\n                    dyUnconsumed -= beforeRange - mOffsetCurrent;\n                    offsetTo(beforeRange);\n                }\n                dyUnconsumed = mDelegateView.consumeScroll(dyUnconsumed);\n                if (dyUnconsumed <= 0) {\n                    return dyUnconsumed;\n                }\n                if (dyUnconsumed == Integer.MAX_VALUE) {\n                    offsetTo(mOffsetRange);\n                } else if (mOffsetCurrent + dyUnconsumed <= mOffsetRange) {\n                    offsetTo(mOffsetCurrent + dyUnconsumed);\n                    return 0;\n                } else {\n                    dyUnconsumed -= mOffsetRange - mOffsetCurrent;\n                    offsetTo(mOffsetRange);\n                    return dyUnconsumed;\n                }\n            }\n        } else if (dyUnconsumed < 0) {\n            if (mDelegateView == null) {\n                if (dyUnconsumed == Integer.MIN_VALUE) {\n                    offsetTo(0);\n                } else if (mOffsetCurrent + dyUnconsumed >= 0) {\n                    offsetTo(mOffsetCurrent + dyUnconsumed);\n                    return 0;\n                } else if (mOffsetCurrent > 0) {\n                    dyUnconsumed += mOffsetCurrent;\n                    offsetTo(0);\n                }\n                return dyUnconsumed;\n            }\n            int afterRange = Math.max(0,\n                    mOffsetRange - getPaddingBottom() - (mFooterView == null ? 0 : mFooterView.getHeight()));\n            if (dyUnconsumed == Integer.MIN_VALUE) {\n                offsetTo(afterRange);\n            } else if (mOffsetCurrent + dyUnconsumed > afterRange) {\n                offsetTo(mOffsetCurrent + dyUnconsumed);\n                return 0;\n            } else if (mOffsetCurrent > afterRange) {\n                dyUnconsumed += mOffsetCurrent - afterRange;\n                offsetTo(afterRange);\n            }\n            dyUnconsumed = mDelegateView.consumeScroll(dyUnconsumed);\n            if (dyUnconsumed >= 0) {\n                return dyUnconsumed;\n            }\n            if (dyUnconsumed == Integer.MIN_VALUE) {\n                offsetTo(0);\n            } else if (mOffsetCurrent + dyUnconsumed > 0) {\n                offsetTo(mOffsetCurrent + dyUnconsumed);\n                return 0;\n            } else if (mOffsetCurrent > 0) {\n                dyUnconsumed += mOffsetCurrent;\n                offsetTo(0);\n            }\n        }\n        return dyUnconsumed;\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        int currentOffset = mOffsetCurrent;\n        if (mDelegateView != null) {\n            currentOffset += mDelegateView.getCurrentScroll();\n        }\n        return currentOffset;\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        int scrollRange = mOffsetRange;\n        if (mDelegateView != null) {\n            scrollRange += mDelegateView.getScrollOffsetRange();\n        }\n        return scrollRange;\n    }\n\n    @Override\n    public void injectScrollNotifier(final OnScrollNotifier notifier) {\n        mScrollNotifier = notifier;\n        if (mDelegateView != null) {\n            mDelegateView.injectScrollNotifier(new OnScrollNotifier() {\n                @Override\n                public void notify(int innerOffset, int innerRange) {\n                    notifier.notify(getCurrentScroll(), getScrollOffsetRange());\n                }\n\n                @Override\n                public void onScrollStateChange(View view, int newScrollState) {\n\n                }\n            });\n        }\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        bundle.putInt(KEY_SCROLL_INFO_OFFSET, -mOffsetCurrent);\n        if (mDelegateView != null) {\n            mDelegateView.saveScrollInfo(bundle);\n        }\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n        int offset = bundle.getInt(KEY_SCROLL_INFO_OFFSET, 0);\n        offsetTo(QMUILangHelper.constrain(-offset, 0, getContainerOffsetRange()));\n        if (mDelegateView != null) {\n            mDelegateView.restoreScrollInfo(bundle);\n        }\n    }\n\n    // NestedScrollingChild2\n\n    @Override\n    public boolean startNestedScroll(int axes, int type) {\n        return mChildHelper.startNestedScroll(axes, type);\n    }\n\n    @Override\n    public void stopNestedScroll(int type) {\n        mChildHelper.stopNestedScroll(type);\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent(int type) {\n        return mChildHelper.hasNestedScrollingParent(type);\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,\n                                        int dyUnconsumed, int[] offsetInWindow, int type) {\n        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                offsetInWindow, type);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,\n                                           int type) {\n        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);\n    }\n\n    // NestedScrollingChild\n\n    @Override\n    public void setNestedScrollingEnabled(boolean enabled) {\n        mChildHelper.setNestedScrollingEnabled(enabled);\n    }\n\n    @Override\n    public boolean isNestedScrollingEnabled() {\n        return mChildHelper.isNestedScrollingEnabled();\n    }\n\n    @Override\n    public boolean startNestedScroll(int axes) {\n        return startNestedScroll(axes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void stopNestedScroll() {\n        stopNestedScroll(ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent() {\n        return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,\n                                        int dyUnconsumed, int[] offsetInWindow) {\n        return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                offsetInWindow, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {\n        return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {\n        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);\n    }\n\n    @Override\n    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {\n        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);\n    }\n\n    // NestedScrollingParent2\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,\n                                       int type) {\n        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;\n    }\n\n    @Override\n    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,\n                                       int type) {\n        mParentHelper.onNestedScrollAccepted(child, target, axes, type);\n        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type);\n    }\n\n    @Override\n    public void onStopNestedScroll(@NonNull View target, int type) {\n        mParentHelper.onStopNestedScroll(target, type);\n        stopNestedScroll(type);\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,\n                               int dyUnconsumed, int type) {\n        int consumed = 0;\n        if (dyUnconsumed > 0) {\n            if (mOffsetCurrent + dyUnconsumed <= mOffsetRange) {\n                consumed = dyUnconsumed;\n                offsetTo(mOffsetCurrent + dyUnconsumed);\n            } else if (mOffsetCurrent <= mOffsetRange) {\n                consumed = mOffsetRange - mOffsetCurrent;\n                offsetTo(mOffsetRange);\n            }\n        } else if (dyUnconsumed < 0) {\n            if (mOffsetCurrent + dyUnconsumed >= 0) {\n                consumed = dyUnconsumed;\n                offsetTo(mOffsetCurrent + dyUnconsumed);\n            } else if (mOffsetCurrent >= 0) {\n                consumed = -mOffsetCurrent;\n                offsetTo(0);\n            }\n        }\n        dispatchNestedScroll(0, dyConsumed + consumed, 0,\n                dyUnconsumed - consumed, null, type);\n    }\n\n    @Override\n    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,\n                                  int type) {\n        dispatchNestedPreScroll(dx, dy, consumed, null, type);\n        int unconsumed = dy - consumed[1];\n        if (unconsumed > 0) {\n            int topMargin = Math.min(mOffsetRange, getPaddingTop() + (mHeaderView == null ? 0 : mHeaderView.getHeight()));\n            if (mOffsetCurrent + unconsumed <= topMargin) {\n                offsetTo(mOffsetCurrent + unconsumed);\n                consumed[1] += unconsumed;\n            } else if (mOffsetCurrent < topMargin) {\n                consumed[1] += topMargin - mOffsetCurrent;\n                offsetTo(topMargin);\n            }\n        } else if (unconsumed < 0) {\n            int bottomMargin = getPaddingBottom() + (mFooterView != null ? mFooterView.getHeight() : 0);\n            if(mOffsetRange > bottomMargin){\n                int b = mOffsetRange - bottomMargin;\n                if (mOffsetCurrent + unconsumed >= b) {\n                    offsetTo(mOffsetCurrent + unconsumed);\n                    consumed[1] += unconsumed;\n                } else if (mOffsetCurrent > b) {\n                    consumed[1] += b - mOffsetCurrent;\n                    offsetTo(b);\n                }\n            }\n        }\n    }\n\n    // NestedScrollingParent\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {\n        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {\n        onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onStopNestedScroll(View target) {\n        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,\n                               int dyUnconsumed) {\n        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,\n                ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {\n        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {\n        return false;\n    }\n\n    @Override\n    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {\n        return dispatchNestedPreFling(velocityX, velocityY);\n    }\n\n    @Override\n    public int getNestedScrollAxes() {\n        return mParentHelper.getNestedScrollAxes();\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedTopLinearLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.layout.QMUILinearLayout;\n\nimport androidx.annotation.NonNull;\n\npublic class QMUIContinuousNestedTopLinearLayout extends QMUILinearLayout implements IQMUIContinuousNestedTopView {\n\n\n    public QMUIContinuousNestedTopLinearLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIContinuousNestedTopLinearLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIContinuousNestedTopLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n\n    @Override\n    public int consumeScroll(int dyUnconsumed) {\n        return dyUnconsumed;\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        return 0;\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        return 0;\n    }\n\n    @Override\n    public void injectScrollNotifier(OnScrollNotifier notifier) {\n\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedTopRecyclerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic class QMUIContinuousNestedTopRecyclerView extends RecyclerView implements IQMUIContinuousNestedTopView {\n    public static final String KEY_SCROLL_INFO_POSITION = \"@qmui_scroll_info_top_rv_pos\";\n    public static final String KEY_SCROLL_INFO_OFFSET = \"@qmui_scroll_info_top_rv_offset\";\n\n    private OnScrollNotifier mScrollNotifier;\n    private final int[] mScrollConsumed = new int[2];\n\n    public QMUIContinuousNestedTopRecyclerView(@NonNull Context context) {\n        this(context, null);\n        init();\n    }\n\n    public QMUIContinuousNestedTopRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n        init();\n    }\n\n    public QMUIContinuousNestedTopRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    private void init(){\n        setVerticalScrollBarEnabled(false);\n    }\n\n    @Override\n    public int consumeScroll(int dyUnconsumed) {\n        if (dyUnconsumed == Integer.MIN_VALUE) {\n            if(canScrollVertically(-1)){\n                scrollToPosition(0);\n            }\n            return Integer.MIN_VALUE;\n        } else if (dyUnconsumed == Integer.MAX_VALUE) {\n            if(canScrollVertically(1)){\n                Adapter adapter = getAdapter();\n                if (adapter != null) {\n                    scrollToPosition(adapter.getItemCount() - 1);\n                }\n            }\n            return Integer.MAX_VALUE;\n        }\n\n        boolean reStartNestedScroll = false;\n        if (!hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {\n            // the scrollBy use ViewCompat.TYPE_TOUCH to handle nested scroll...\n            reStartNestedScroll = true;\n            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);\n\n            // and scrollBy only call dispatchNestedScroll, not call dispatchNestedPreScroll\n            mScrollConsumed[0] = 0;\n            mScrollConsumed[1] = 0;\n            dispatchNestedPreScroll(0, dyUnconsumed, mScrollConsumed, null, ViewCompat.TYPE_TOUCH);\n            dyUnconsumed -= mScrollConsumed[1];\n        }\n        scrollBy(0, dyUnconsumed);\n        if (reStartNestedScroll) {\n            stopNestedScroll(ViewCompat.TYPE_TOUCH);\n        }\n        return 0;\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        return computeVerticalScrollOffset();\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        return Math.max(0, computeVerticalScrollRange() - getHeight());\n    }\n\n    @Override\n    public void injectScrollNotifier(OnScrollNotifier notifier) {\n        mScrollNotifier = notifier;\n    }\n\n    @Override\n    public void onScrolled(int dx, int dy) {\n        super.onScrolled(dx, dy);\n        if(mScrollNotifier != null){\n            mScrollNotifier.notify(getCurrentScroll(), getScrollOffsetRange());\n        }\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        LayoutManager layoutManager = getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            LinearLayoutManager lm = (LinearLayoutManager) layoutManager;\n            int pos = lm.findFirstVisibleItemPosition();\n            View firstView = lm.findViewByPosition(pos);\n            int offset = firstView == null ? 0 : firstView.getTop();\n            bundle.putInt(KEY_SCROLL_INFO_POSITION, pos);\n            bundle.putInt(KEY_SCROLL_INFO_OFFSET, offset);\n        }\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n        LayoutManager layoutManager = getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            int pos = bundle.getInt(KEY_SCROLL_INFO_POSITION, 0);\n            int offset = bundle.getInt(KEY_SCROLL_INFO_OFFSET, 0);\n            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(pos, offset);\n            if(mScrollNotifier != null){\n                mScrollNotifier.notify(getCurrentScroll(), getScrollOffsetRange());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedTopWebView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\n\npublic class QMUIContinuousNestedTopWebView extends QMUIWebView implements IQMUIContinuousNestedTopView {\n\n    public static final String KEY_SCROLL_INFO = \"@qmui_scroll_info_top_webview\";\n\n    private OnScrollNotifier mScrollNotifier;\n\n    public QMUIContinuousNestedTopWebView(Context context) {\n        super(context);\n        init();\n    }\n\n    public QMUIContinuousNestedTopWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public QMUIContinuousNestedTopWebView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init(){\n        setVerticalScrollBarEnabled(false);\n    }\n\n    @Override\n    public int consumeScroll(int yUnconsumed) {\n        // compute the consumed value\n        int scrollY = getScrollY();\n        int maxScrollY = getScrollOffsetRange();\n        // the scrollY may be negative or larger than scrolling range\n        scrollY = Math.max(0, Math.min(scrollY, maxScrollY));\n        int dy = 0;\n        if (yUnconsumed < 0) {\n            dy = Math.max(yUnconsumed, -scrollY);\n        } else if (yUnconsumed > 0) {\n            dy = Math.min(yUnconsumed, maxScrollY - scrollY);\n        }\n        scrollBy(0, dy);\n        return yUnconsumed - dy;\n    }\n\n    @Override\n    public int getCurrentScroll() {\n        int scrollY = getScrollY();\n        int scrollRange = getScrollOffsetRange();\n        return Math.max(0, Math.min(scrollY, scrollRange));\n    }\n\n    @Override\n    public int getScrollOffsetRange() {\n        return computeVerticalScrollRange() - getHeight();\n    }\n\n    @Override\n    public void injectScrollNotifier(OnScrollNotifier notifier) {\n        mScrollNotifier = notifier;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (mScrollNotifier != null) {\n            mScrollNotifier.notify(getCurrentScroll(), getScrollOffsetRange());\n        }\n    }\n\n    @Override\n    public void saveScrollInfo(@NonNull Bundle bundle) {\n        bundle.putInt(KEY_SCROLL_INFO, getScrollY());\n    }\n\n    @Override\n    public void restoreScrollInfo(@NonNull Bundle bundle) {\n        int scrollY = QMUIDisplayHelper.px2dp(getContext(),\n                bundle.getInt(KEY_SCROLL_INFO, 0));\n        exec(\"javascript:scrollTo(0, \" + scrollY + \")\");\n    }\n\n    private void exec(final String jsCode) {\n        evaluateJavascript(jsCode, null);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIDraggableScrollBar.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2015 The Android Open Source Project\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.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\npublic class QMUIDraggableScrollBar extends View {\n\n    private int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};\n    private int[] STATE_NORMAL = new int[]{};\n\n    private Drawable mDragDrawable;\n    private int mKeepShownTime = 800;\n    private int mTransitionDuration = 100;\n    private long mStartTransitionTime = 0;\n    private float mCurrentAlpha = 0f;\n    private float mPercent = 0f;\n    private Runnable mDelayInvalidateRunnable = new Runnable() {\n        @Override\n        public void run() {\n            invalidate();\n        }\n    };\n    private boolean mIsInDragging = false;\n    private Callback mCallback;\n    private int mDrawableDrawTop = -1;\n    private float mDragInnerTop = 0;\n    private int mAdjustDistanceProtection = QMUIDisplayHelper.dp2px(getContext(), 20);\n    private int mAdjustMaxDistanceOnce = QMUIDisplayHelper.dp2px(getContext(), 4);\n    private boolean mAdjustDistanceWithAnimation = true;\n    private boolean enableFadeInAndOut = true;\n\n    public QMUIDraggableScrollBar(Context context) {\n        this(context, (AttributeSet) null);\n    }\n\n    public QMUIDraggableScrollBar(Context context, @NonNull Drawable dragDrawable) {\n        this(context, (AttributeSet) null);\n        mDragDrawable = dragDrawable.mutate();\n    }\n\n    public QMUIDraggableScrollBar(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    public void setAdjustDistanceWithAnimation(boolean adjustDistanceWithAnimation) {\n        mAdjustDistanceWithAnimation = adjustDistanceWithAnimation;\n    }\n\n    public void setKeepShownTime(int keepShownTime) {\n        mKeepShownTime = keepShownTime;\n    }\n\n    public void setTransitionDuration(int transitionDuration) {\n        mTransitionDuration = transitionDuration;\n    }\n\n    public void setEnableFadeInAndOut(boolean enableFadeInAndOut) {\n        this.enableFadeInAndOut = enableFadeInAndOut;\n    }\n\n    public boolean isEnableFadeInAndOut() {\n        return enableFadeInAndOut;\n    }\n\n    public void setDragDrawable(Drawable dragDrawable) {\n        mDragDrawable = dragDrawable.mutate();\n        invalidate();\n    }\n\n    public void setPercent(float percent) {\n        if(!mIsInDragging){\n            setPercentInternal(percent);\n        }\n    }\n\n    private void setPercentInternal(float percent){\n        mPercent = percent;\n        invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        Drawable drawable = mDragDrawable;\n        if (drawable == null) {\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n            return;\n        }\n        super.onMeasure(MeasureSpec.makeMeasureSpec(\n                drawable.getIntrinsicWidth(), MeasureSpec.EXACTLY), heightMeasureSpec);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        Drawable drawable = mDragDrawable;\n        if (drawable == null) {\n            return super.onTouchEvent(event);\n        }\n        int action = event.getAction();\n        final float x = event.getX();\n        final float y = event.getY();\n        if (action == MotionEvent.ACTION_DOWN) {\n            mIsInDragging = false;\n            if (mCurrentAlpha > 0 && x > getWidth() - drawable.getIntrinsicWidth()\n                    && y >= mDrawableDrawTop && y <= mDrawableDrawTop + drawable.getIntrinsicHeight()) {\n                mDragInnerTop = y - mDrawableDrawTop;\n                getParent().requestDisallowInterceptTouchEvent(true);\n                mIsInDragging = true;\n                if(mCallback != null){\n                    mCallback.onDragStarted();\n                    mDragDrawable.setState(STATE_PRESSED);\n                }\n            }\n        } else if (action == MotionEvent.ACTION_MOVE) {\n            if (mIsInDragging) {\n                getParent().requestDisallowInterceptTouchEvent(true);\n                onDragging(drawable, y);\n            }\n        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n            if (mIsInDragging) {\n                mIsInDragging = false;\n                onDragging(drawable, y);\n                if(mCallback != null){\n                    mCallback.onDragEnd();\n                    mDragDrawable.setState(STATE_NORMAL);\n                }\n            }\n        }\n        return mIsInDragging;\n    }\n\n    private void onDragging(Drawable drawable, float currentY) {\n        float percent = (currentY - getScrollBarTopMargin() - mDragInnerTop) / (getHeight() - getScrollBarBottomMargin() - getScrollBarTopMargin() - drawable.getIntrinsicHeight());\n        percent = QMUILangHelper.constrain(percent, 0f, 1f);\n        if (mCallback != null) {\n            mCallback.onDragToPercent(percent);\n        }\n        setPercentInternal(percent);\n    }\n\n    public void awakenScrollBar() {\n        if (mDragDrawable == null) {\n            mDragDrawable = ContextCompat.getDrawable(getContext(), R.drawable.qmui_icon_scroll_bar);\n        }\n        long current = System.currentTimeMillis();\n        if (current - mStartTransitionTime > mTransitionDuration) {\n            mStartTransitionTime = current - mTransitionDuration;\n        }\n        ViewCompat.postInvalidateOnAnimation(this);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        Drawable drawable = mDragDrawable;\n        if (drawable == null) {\n            return;\n        }\n        int drawableWidth = drawable.getIntrinsicWidth();\n        int drawableHeight = drawable.getIntrinsicHeight();\n        if (drawableWidth <= 0 || drawableHeight <= 0) {\n            return;\n        }\n\n        int needInvalidate = -1;\n        if(enableFadeInAndOut){\n            long timeAfterStartShow = System.currentTimeMillis() - mStartTransitionTime;\n            long timeAfterEndShow;\n            if (timeAfterStartShow < mTransitionDuration) {\n                // in show animation\n                mCurrentAlpha = timeAfterStartShow * 1f / mTransitionDuration;\n                needInvalidate = 0;\n            } else if (timeAfterStartShow - mTransitionDuration < mKeepShownTime) {\n                // keep show\n                mCurrentAlpha = 1f;\n                needInvalidate = (int) (mKeepShownTime - (timeAfterStartShow - mTransitionDuration));\n            } else if ((timeAfterEndShow = timeAfterStartShow - mTransitionDuration - mKeepShownTime)\n                    < mTransitionDuration) {\n                // in hide animation\n                mCurrentAlpha = 1 - timeAfterEndShow * 1f / mTransitionDuration;\n                needInvalidate = 0;\n            } else {\n                mCurrentAlpha = 0f;\n            }\n            if (mCurrentAlpha <= 0f) {\n                return;\n            }\n        }else{\n            mCurrentAlpha = 1f;\n        }\n        drawable.setAlpha((int) (mCurrentAlpha * 255));\n\n        int totalHeight = getHeight() - getScrollBarTopMargin() - getScrollBarBottomMargin();\n        int totalWidth = getWidth();\n        int top = getScrollBarTopMargin() + (int) ((totalHeight - drawableHeight) * mPercent);\n        int left = totalWidth - drawableWidth;\n        if (!mIsInDragging && mDrawableDrawTop > 0 && mAdjustDistanceWithAnimation) {\n            int moveDistance = top - mDrawableDrawTop;\n            if (moveDistance > mAdjustMaxDistanceOnce && moveDistance < mAdjustDistanceProtection) {\n                top = mDrawableDrawTop + mAdjustMaxDistanceOnce;\n                needInvalidate = 0;\n            } else if (moveDistance < -mAdjustMaxDistanceOnce && moveDistance > -mAdjustDistanceProtection) {\n                top = mDrawableDrawTop - mAdjustMaxDistanceOnce;\n                needInvalidate = 0;\n            }\n        }\n        drawable.setBounds(0, 0, drawableWidth, drawableHeight);\n        canvas.save();\n        canvas.translate(left, top);\n        drawable.draw(canvas);\n        canvas.restore();\n        mDrawableDrawTop = top;\n\n        if (needInvalidate == 0) {\n            invalidate();\n        } else if (needInvalidate > 0) {\n            ViewCompat.postOnAnimationDelayed(this, mDelayInvalidateRunnable, needInvalidate);\n        }\n    }\n\n    protected int getScrollBarTopMargin() {\n        return 0;\n    }\n\n    protected int getScrollBarBottomMargin() {\n        return 0;\n    }\n\n    interface Callback {\n        void onDragStarted();\n        void onDragToPercent(float percent);\n        void onDragEnd();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIViewOffsetBehavior.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2015 The Android Open Source Project\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.qmuiteam.qmui.nestedScroll;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\n\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\npublic class QMUIViewOffsetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {\n\n    private QMUIViewOffsetHelper viewOffsetHelper;\n\n    private int tempTopBottomOffset = 0;\n    private int tempLeftRightOffset = 0;\n\n    public QMUIViewOffsetBehavior() {\n    }\n\n    public QMUIViewOffsetBehavior(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {\n        // First let lay the child out\n        layoutChild(parent, child, layoutDirection);\n\n        if (viewOffsetHelper == null) {\n            viewOffsetHelper = new QMUIViewOffsetHelper(child);\n        }\n        viewOffsetHelper.onViewLayout();\n\n        if (tempTopBottomOffset != 0) {\n            viewOffsetHelper.setTopAndBottomOffset(tempTopBottomOffset);\n            tempTopBottomOffset = 0;\n        }\n        if (tempLeftRightOffset != 0) {\n            viewOffsetHelper.setLeftAndRightOffset(tempLeftRightOffset);\n            tempLeftRightOffset = 0;\n        }\n\n        return true;\n    }\n\n    protected void layoutChild(CoordinatorLayout parent, V child, int layoutDirection) {\n        // Let the parent lay it out by default\n        parent.onLayoutChild(child, layoutDirection);\n    }\n\n    public boolean setTopAndBottomOffset(int offset) {\n        if (viewOffsetHelper != null) {\n            return viewOffsetHelper.setTopAndBottomOffset(offset);\n        } else {\n            tempTopBottomOffset = offset;\n        }\n        return false;\n    }\n\n    public boolean setLeftAndRightOffset(int offset) {\n        if (viewOffsetHelper != null) {\n            return viewOffsetHelper.setLeftAndRightOffset(offset);\n        } else {\n            tempLeftRightOffset = offset;\n        }\n        return false;\n    }\n\n    public int getTopAndBottomOffset() {\n        return viewOffsetHelper != null ? viewOffsetHelper.getTopAndBottomOffset() : 0;\n    }\n\n    public int getLeftAndRightOffset() {\n        return viewOffsetHelper != null ? viewOffsetHelper.getLeftAndRightOffset() : 0;\n    }\n\n    public void setVerticalOffsetEnabled(boolean verticalOffsetEnabled) {\n        if (viewOffsetHelper != null) {\n            viewOffsetHelper.setVerticalOffsetEnabled(verticalOffsetEnabled);\n        }\n    }\n\n    public int getLayoutTop() {\n        if (viewOffsetHelper != null) {\n            return viewOffsetHelper.getLayoutTop();\n        }\n        return 0;\n    }\n\n    public int getLayoutLeft() {\n        if (viewOffsetHelper != null) {\n            return viewOffsetHelper.getLayoutLeft();\n        }\n        return 0;\n    }\n\n    public boolean isVerticalOffsetEnabled() {\n        return viewOffsetHelper != null && viewOffsetHelper.isVerticalOffsetEnabled();\n    }\n\n    public void setHorizontalOffsetEnabled(boolean horizontalOffsetEnabled) {\n        if (viewOffsetHelper != null) {\n            viewOffsetHelper.setHorizontalOffsetEnabled(horizontalOffsetEnabled);\n        }\n    }\n\n    public boolean isHorizontalOffsetEnabled() {\n        return viewOffsetHelper != null && viewOffsetHelper.isHorizontalOffsetEnabled();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/qqface/IQMUIQQFaceManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.qqface;\n\nimport android.graphics.drawable.Drawable;\n\n/**\n * QMUIQQFace资源管理接口，其实现参考QMUIDemo\n * 1. 不想将所有emoji表情资源全都打包到qmui中\n * 2. 使用者可以高度自定义表情资源\n *\n * @author cginechen\n * @date 2016-12-21\n */\n\npublic interface IQMUIQQFaceManager {\n\n    /**\n     * 判断是否是SoftBank编码表情，用于缩小解析范围，返回true调用{@link #getSoftbankEmojiResource}进行解析\n     * SoftBank虽然是Unicode编码，但是位于BMP，所以可以直接通过char来做判断\n     * <p>\n     * Android很多输入法都自带一套SoftBank表情，但不同输入法的表情并不统一。\n     */\n    boolean maybeSoftBankEmoji(char c);\n\n    /**\n     * 获取SoftBank编码表情，如果没有则返回0\n     */\n    int getSoftbankEmojiResource(char c);\n\n    /**\n     * 判断Unicode编码字符是否是表情，用于缩小解析范围，返回true调用{@link #getEmojiResource}进行解析\n     */\n    boolean maybeEmoji(int codePoint);\n\n    /**\n     * 获取Unicode编码表情，如果没有则返回0\n     */\n    int getEmojiResource(int codePoint);\n\n    /**\n     * 获取双字符编码表情， 如果没有则返回0\n     */\n    int getDoubleUnicodeEmoji(int currentCodePoint, int nextCodePoint);\n\n    /**\n     * 将字符串解析例为表情，如果没有则返回0\n     */\n    int getQQfaceResource(CharSequence text);\n\n    /**\n     * 寻找特殊bounds的Drawable, 字符串请以[开头和以]结尾\n     */\n    Drawable getSpecialBoundsDrawable(CharSequence text);\n\n    /**\n     * 获取特殊bounds的Drawable的最高height, 这将决定行高\n     * fixme: 目前会影响所有行，要改为只影响含有特殊Drawable的行\n     */\n    int getSpecialDrawableMaxHeight();\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/qqface/QMUINoQQFaceManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.qqface;\n\nimport android.graphics.drawable.Drawable;\n\npublic class QMUINoQQFaceManager implements IQMUIQQFaceManager{\n\n    @Override\n    public boolean maybeSoftBankEmoji(char c) {\n        return false;\n    }\n\n    @Override\n    public int getSoftbankEmojiResource(char c) {\n        return 0;\n    }\n\n    @Override\n    public boolean maybeEmoji(int codePoint) {\n        return false;\n    }\n\n    @Override\n    public int getEmojiResource(int codePoint) {\n        return 0;\n    }\n\n    @Override\n    public int getDoubleUnicodeEmoji(int currentCodePoint, int nextCodePoint) {\n        return 0;\n    }\n\n    @Override\n    public int getQQfaceResource(CharSequence text) {\n        return 0;\n    }\n\n    @Override\n    public Drawable getSpecialBoundsDrawable(CharSequence text) {\n        return null;\n    }\n\n    @Override\n    public int getSpecialDrawableMaxHeight() {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/qqface/QMUIQQFaceCompiler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.qqface;\n\nimport android.graphics.drawable.Drawable;\nimport android.text.Spannable;\nimport android.util.LruCache;\n\nimport androidx.annotation.MainThread;\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * {@link QMUIQQFaceView} 的内容解析器，将文本内容解析成 {@link QMUIQQFaceView} 想要的数据格式。\n *\n * @author cginechen\n * @date 2016-12-21\n */\n\npublic class QMUIQQFaceCompiler {\n    private static final int SPAN_COLUMN = 2;\n    private static final Map<IQMUIQQFaceManager, QMUIQQFaceCompiler> sInstanceMap = new HashMap<>(4);\n    private static IQMUIQQFaceManager sDefaultQQFaceManager = new QMUINoQQFaceManager();\n\n    public static void setDefaultQQFaceManager(@NonNull IQMUIQQFaceManager defaultQQFaceManager) {\n        sDefaultQQFaceManager = defaultQQFaceManager;\n    }\n\n    private LruCache<CharSequence, ElementList> mCache;\n    private IQMUIQQFaceManager mQQFaceManager;\n\n\n    @MainThread\n    public static QMUIQQFaceCompiler getDefaultInstance(){\n        return getInstance(sDefaultQQFaceManager);\n    }\n\n    @MainThread\n    public static QMUIQQFaceCompiler getInstance(IQMUIQQFaceManager manager) {\n        QMUIQQFaceCompiler instance = sInstanceMap.get(manager);\n        if (instance != null) {\n            return instance;\n        }\n        instance = new QMUIQQFaceCompiler(manager);\n        sInstanceMap.put(manager, instance);\n        return instance;\n    }\n\n    private QMUIQQFaceCompiler(IQMUIQQFaceManager manager) {\n        mCache = new LruCache<>(30);\n        mQQFaceManager = manager;\n    }\n\n    public int getSpecialBoundsMaxHeight() {\n        return mQQFaceManager.getSpecialDrawableMaxHeight();\n    }\n\n    public ElementList compile(CharSequence text) {\n        if (QMUILangHelper.isNullOrEmpty(text)) {\n            return null;\n        }\n        return compile(text, 0, text.length());\n    }\n\n    public ElementList compile(CharSequence text, int start, int end) {\n        return compile(text, start, end, false);\n    }\n\n    private ElementList compile(CharSequence text, int start, int end, boolean inSpan) {\n        if (QMUILangHelper.isNullOrEmpty(text)) {\n            return null;\n        }\n        if (start < 0 || start >= text.length()) {\n            throw new IllegalArgumentException(\"start must >= 0 and < text.length\");\n        }\n        if (end <= start) {\n            throw new IllegalArgumentException(\"end must > start\");\n        }\n        int size = text.length();\n\n        if (end > size) {\n            end = size;\n        }\n\n        boolean hasClickableSpans = false;\n        QMUITouchableSpan[] spans = null;\n        int[] spanInfo = null;\n        if (!inSpan && (text instanceof Spannable)) {\n            final Spannable spannable = (Spannable) text;\n            spans = ((Spannable) text).getSpans(\n                    0,\n                    text.length() - 1,\n                    QMUITouchableSpan.class);\n            Arrays.sort(spans, new Comparator<QMUITouchableSpan>() {\n                @Override\n                public int compare(QMUITouchableSpan o1, QMUITouchableSpan o2) {\n                    int start1 = spannable.getSpanStart(o1);\n                    int start2 = spannable.getSpanStart(o2);\n                    if (start1 > start2) {\n                        return 1;\n                    } else if (start1 == start2) {\n                        return 0;\n                    }\n                    return -1;\n                }\n            });\n            hasClickableSpans = spans.length > 0;\n            if (hasClickableSpans) {\n                spanInfo = new int[spans.length * SPAN_COLUMN];\n                for (int i = 0; i < spans.length; i++) {\n                    spanInfo[i * SPAN_COLUMN] = spannable.getSpanStart(spans[i]);\n                    spanInfo[i * SPAN_COLUMN + 1] = spannable.getSpanEnd(spans[i]);\n                }\n            }\n        }\n\n        ElementList elementList = mCache.get(text);\n        if (!hasClickableSpans && elementList != null && start == elementList.getStart() && end == elementList.getEnd()) {\n            return elementList;\n        }\n        elementList = realCompile(text, start, end, spans, spanInfo);\n        if(!hasClickableSpans && !inSpan){\n            mCache.put(text, elementList);\n        }\n        return elementList;\n    }\n\n    public void setCache(LruCache<CharSequence, ElementList> cache) {\n        mCache = cache;\n    }\n\n    @SuppressWarnings(\"ConstantConditions\")\n    private ElementList realCompile(CharSequence text, int start, int end, QMUITouchableSpan[] spans, int[] spanInfo) {\n        int size = text.length();\n        int nearSpanIndex = -1;\n        int nearSpanStart = Integer.MAX_VALUE;\n        int nearSpanEnd = nearSpanStart;\n        if (spans != null && spans.length > 0) {\n            nearSpanIndex = 0;\n            nearSpanStart = spanInfo[0];\n            nearSpanEnd = spanInfo[1];\n        }\n\n        ElementList elementList = new ElementList(start, end);\n        if (start > 0) {\n            elementList.add(Element.createTextElement(text.subSequence(0, start)));\n        }\n        int index = start, last = start;\n        boolean inParentheses = false;\n        while (index < end) {\n            // 优先处理Span的情况\n            if (index == nearSpanStart) {\n                if (index - last > 0) {\n                    if (inParentheses) {\n                        inParentheses = false;\n                        last--;\n                    }\n                    elementList.add(Element.createTextElement(text.subSequence(last, index)));\n                }\n                elementList.add(Element.createTouchSpanElement(\n                        text.subSequence(nearSpanStart, nearSpanEnd), spans[nearSpanIndex], this));\n                index = last = nearSpanEnd;\n                nearSpanIndex++;\n                if (nearSpanIndex >= spans.length) {\n                    nearSpanStart = nearSpanEnd = Integer.MAX_VALUE;\n                } else {\n                    nearSpanStart = spanInfo[nearSpanIndex * SPAN_COLUMN];\n                    nearSpanEnd = spanInfo[nearSpanIndex * SPAN_COLUMN + 1];\n                }\n\n                continue;\n            }\n\n            char c = text.charAt(index);\n            if (c == '[') {\n                if (index - last > 0) {\n                    elementList.add(Element.createTextElement(text.subSequence(last, index)));\n                }\n                inParentheses = true;\n                last = index++;\n                continue;\n            } else if (c == ']' && inParentheses) {\n                inParentheses = false;\n                index++;\n                if (index - last > 0) {\n                    String label = text.subSequence(last, index).toString();\n                    Drawable specialDrawable = mQQFaceManager.getSpecialBoundsDrawable(label);\n                    if (specialDrawable != null) {\n                        elementList.add(Element.createSpeaicalBoundsDrawableElement(specialDrawable));\n                        last = index;\n                    } else {\n                        int res = mQQFaceManager.getQQfaceResource(label);\n                        if (res != 0) {\n                            elementList.add(Element.createDrawableElement(res));\n                            last = index;\n                        }\n                    }\n                }\n                continue;\n            } else if (c == '\\n') {\n                if (inParentheses) {\n                    inParentheses = false;\n                }\n                if (index - last > 0) {\n                    elementList.add(Element.createTextElement(text.subSequence(last, index)));\n                }\n                elementList.add(Element.createNextLineElement());\n                last = ++index;\n                continue;\n            }\n            if (inParentheses) {\n                if (index - last > 8) {\n                    inParentheses = false;\n                } else {\n                    index++;\n                    continue;\n                }\n\n\n            }\n\n            int skip = 0;\n            int icon = 0;\n            if (mQQFaceManager.maybeSoftBankEmoji(c)) {\n                icon = mQQFaceManager.getSoftbankEmojiResource(c);\n                skip = icon == 0 ? 0 : 1;\n            }\n            if (icon == 0) {\n                int unicode = Character.codePointAt(text, index);\n                skip = Character.charCount(unicode);\n                if (mQQFaceManager.maybeEmoji(unicode)) {\n                    icon = mQQFaceManager.getEmojiResource(unicode);\n                }\n                if (icon == 0 && start + skip < end) {\n                    int nextUnicode = Character.codePointAt(text, start + skip);\n                    icon = mQQFaceManager.getDoubleUnicodeEmoji(unicode, nextUnicode);\n                    if (icon != 0) {\n                        skip += Character.charCount(nextUnicode);\n                    }\n                }\n            }\n            if (icon != 0) {\n                if (last != index) {\n                    elementList.add(Element.createTextElement(text.subSequence(last, index)));\n                }\n                elementList.add(Element.createDrawableElement(icon));\n                index += skip;\n                last = index;\n            } else {\n                index++;\n            }\n        }\n        if (last < end) {\n            elementList.add(Element.createTextElement(text.subSequence(last, size)));\n        }\n        return elementList;\n    }\n\n    public enum ElementType {\n        TEXT,\n        DRAWABLE,\n        SPECIAL_BOUNDS_DRAWABLE,\n        SPAN,\n        NEXTLINE\n    }\n\n    public static class Element {\n        private ElementType mType;\n        private CharSequence mText;\n        private int mDrawableRes;\n        private Drawable mSpecialBoundsDrawable;\n        private ElementList mChildList; // for span\n        private QMUITouchableSpan mTouchableSpan;\n\n        public ElementType getType() {\n            return mType;\n        }\n\n        public CharSequence getText() {\n            return mText;\n        }\n\n        public int getDrawableRes() {\n            return mDrawableRes;\n        }\n\n        public ElementList getChildList() {\n            return mChildList;\n        }\n\n        public QMUITouchableSpan getTouchableSpan() {\n            return mTouchableSpan;\n        }\n\n        public Drawable getSpecialBoundsDrawable() {\n            return mSpecialBoundsDrawable;\n        }\n\n        public static Element createTextElement(CharSequence text) {\n            Element element = new Element();\n            element.mType = ElementType.TEXT;\n            element.mText = text;\n            return element;\n        }\n\n        public static Element createDrawableElement(int drawableRes) {\n            Element element = new Element();\n            element.mType = ElementType.DRAWABLE;\n            element.mDrawableRes = drawableRes;\n            return element;\n        }\n\n        public static Element createSpeaicalBoundsDrawableElement(Drawable specialBoundsDrawable) {\n            Element element = new Element();\n            element.mType = ElementType.SPECIAL_BOUNDS_DRAWABLE;\n            element.mSpecialBoundsDrawable = specialBoundsDrawable;\n            return element;\n        }\n\n        public static Element createTouchSpanElement(CharSequence text,\n                                                     QMUITouchableSpan touchableSpan,\n                                                     QMUIQQFaceCompiler compiler) {\n            Element element = new Element();\n            element.mType = ElementType.SPAN;\n            element.mChildList = compiler.compile(text, 0, text.length(), true);\n            element.mTouchableSpan = touchableSpan;\n            return element;\n        }\n\n        public static Element createNextLineElement() {\n            Element element = new Element();\n            element.mType = ElementType.NEXTLINE;\n            return element;\n        }\n    }\n\n\n    public static class ElementList {\n        private int mStart;\n        private int mEnd;\n        private int mQQFaceCount = 0;\n        private int mNewLineCount = 0;\n        private List<Element> mElements;\n\n        public ElementList(int start, int end) {\n            mStart = start;\n            mEnd = end;\n            mElements = new ArrayList<>();\n        }\n\n        public int getStart() {\n            return mStart;\n        }\n\n        public int getEnd() {\n            return mEnd;\n        }\n\n        public int getNewLineCount() {\n            return mNewLineCount;\n        }\n\n        public int getQQFaceCount() {\n            return mQQFaceCount;\n        }\n\n        public void add(Element element) {\n            if (element.getType() == ElementType.DRAWABLE) {\n                mQQFaceCount++;\n            } else if (element.getType() == ElementType.NEXTLINE) {\n                mNewLineCount++;\n            } else if (element.getType() == ElementType.SPAN) {\n                ElementList childList = element.getChildList();\n                if (childList != null) {\n                    mQQFaceCount += element.getChildList().getQQFaceCount();\n                    mNewLineCount += element.getChildList().getNewLineCount();\n                }\n            }\n            mElements.add(element);\n        }\n\n        public List<Element> getElements() {\n            return mElements;\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/qqface/QMUIQQFaceView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.qqface;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.accessibility.AccessibilityNodeInfo;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.link.ITouchableSpan;\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\nimport java.lang.ref.WeakReference;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport static android.view.View.MeasureSpec.AT_MOST;\n\n/**\n * 表情控件\n * <p>\n * <ul>\n * <li>支持显示表情的伪 {@link android.widget.TextView}（继续自定义 {@link View}，而不是真正的 {@link android.widget.TextView})，\n * 实现了 {@link android.widget.TextView} 的 maxLine、ellipsize、textSize、textColor 等基本功能。</li>\n * <li>支持与 {@link QMUITouchableSpan} 配合使用实现内容可点击。</li>\n * </ul>\n *\n * @author cginechen\n * @date 2016-12-21\n */\n\npublic class QMUIQQFaceView extends View {\n    private static final String TAG = \"QMUIQQFaceView\";\n    private CharSequence mOriginText;\n    private QMUIQQFaceCompiler.ElementList mElementList;\n    private QMUIQQFaceCompiler mCompiler;\n    private boolean mOpenQQFace = true;\n    private TextPaint mPaint;\n    private Paint mDecorationPaint;\n    private int mTextSize;\n    private ColorStateList mTextColor;\n    private int mLineSpace = -1;\n    private int mFontHeight;\n    private int mQQFaceSize = 0;\n    private int mFirstBaseLine;\n    private int mMaxLine = Integer.MAX_VALUE;\n    private boolean mIsSingleLine = false;\n    private int mLines = 0;\n    private HashMap<QMUIQQFaceCompiler.Element, SpanInfo> mSpanInfos = new HashMap<>();\n    private boolean mIsTouchDownInMoreText = false;\n    private Rect mMoreHitRect = new Rect();\n    private static final String mEllipsizeText = \"...\";\n    private String mMoreActionText;\n    private ColorStateList mMoreActionColor;\n    private ColorStateList mMoreActionBgColor;\n    private int mMoreActionTextLength = 0;\n    private int mEllipsizeTextLength = 0;\n    private TextUtils.TruncateAt mEllipsize = TextUtils.TruncateAt.END;\n    private boolean mIsNeedEllipsize = false;\n    private int mNeedDrawLine = 0;\n    private int mParagraphShowCount = 0;\n    private int mQQFaceSizeAddon = 0; // 可以让QQ表情高度比字体高度小一点或大一点\n    private QQFaceViewListener mListener;\n    private int mMaxWidth = Integer.MAX_VALUE;\n    private PressCancelAction mPendingPressCancelAction = null;\n    private boolean mJumpHandleMeasureAndDraw = false;\n    private boolean mIncludePad = true;\n    private Typeface mTypeface = null;\n    private int mParagraphSpace = 0; // 段间距\n    private int mSpecialDrawablePadding = 0;\n    private int mGravity = Gravity.NO_GRAVITY;\n    private final int[] mPressedState = new int[]{\n            android.R.attr.state_pressed,\n            android.R.attr.state_enabled\n    };\n    private boolean mIsNeedUnderlineForMoreText = false;\n    private ColorStateList mLinkUnderLineColor;\n    private int mLinkUnderLineHeight = 1;\n\n    public QMUIQQFaceView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIQQFaceView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIQQFaceStyle);\n    }\n\n    public QMUIQQFaceView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = getContext().obtainStyledAttributes(attrs,\n                R.styleable.QMUIQQFaceView, defStyleAttr, 0);\n        mQQFaceSizeAddon = -QMUIDisplayHelper.dp2px(context, 2); // 默认表情小一点好看\n        mTextSize = array.getDimensionPixelSize(R.styleable.QMUIQQFaceView_android_textSize,\n                QMUIDisplayHelper.dp2px(context, 14));\n        mTextColor = array.getColorStateList(R.styleable.QMUIQQFaceView_android_textColor);\n        mIsSingleLine = array.getBoolean(R.styleable.QMUIQQFaceView_android_singleLine, false);\n        mMaxLine = array.getInt(R.styleable.QMUIQQFaceView_android_maxLines, mMaxLine);\n        int lineSpace = array.getDimensionPixelOffset(R.\n                styleable.QMUIQQFaceView_android_lineSpacingExtra, 0);\n        setLineSpace(lineSpace);\n        int ellipsize = -1;\n        ellipsize = array.getInt(R.styleable.QMUIQQFaceView_android_ellipsize, ellipsize);\n        switch (ellipsize) {\n            case 1:\n                mEllipsize = TextUtils.TruncateAt.START;\n                break;\n            case 2:\n                mEllipsize = TextUtils.TruncateAt.MIDDLE;\n                break;\n            case 3:\n                mEllipsize = TextUtils.TruncateAt.END;\n                break;\n            default:\n                mEllipsize = null;\n                break;\n        }\n        mMaxWidth = array.getDimensionPixelSize(R.styleable.QMUIQQFaceView_android_maxWidth, mMaxWidth);\n        mSpecialDrawablePadding = array.getDimensionPixelSize(R.styleable.QMUIQQFaceView_qmui_special_drawable_padding, 0);\n        final String text = array.getString(R.styleable.QMUIQQFaceView_android_text);\n        if (!QMUILangHelper.isNullOrEmpty(text)) {\n            mOriginText = text;\n        }\n        mMoreActionText = array.getString(R.styleable.QMUIQQFaceView_qmui_more_action_text);\n        mMoreActionColor = array.getColorStateList(R.styleable.QMUIQQFaceView_qmui_more_action_color);\n        mMoreActionBgColor = array.getColorStateList(R.styleable.QMUIQQFaceView_qmui_more_action_bg_color);\n\n        array.recycle();\n        mPaint = new TextPaint();\n        mPaint.setAntiAlias(true);\n        mPaint.setTextSize(mTextSize);\n        mEllipsizeTextLength = (int) Math.ceil(mPaint.measureText(mEllipsizeText));\n        measureMoreActionTextLength();\n        mDecorationPaint = new Paint();\n        mDecorationPaint.setAntiAlias(true);\n        mDecorationPaint.setStyle(Paint.Style.FILL);\n        setCompiler(QMUIQQFaceCompiler.getDefaultInstance());\n    }\n\n    public void setOpenQQFace(boolean openQQFace) {\n        mOpenQQFace = openQQFace;\n    }\n\n    public void setGravity(int gravity) {\n        mGravity = gravity;\n    }\n\n    public int getGravity() {\n        return mGravity;\n    }\n\n    public void setMaxWidth(int maxWidth) {\n        if (mMaxWidth != maxWidth) {\n            mMaxWidth = maxWidth;\n            requestLayout();\n        }\n    }\n\n    public int getMaxWidth() {\n        return mMaxWidth;\n    }\n\n    SpanInfo mTouchSpanInfo = null;\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        final int x = (int) event.getX();\n        final int y = (int) event.getY();\n\n        if (mSpanInfos.isEmpty() && mMoreHitRect.isEmpty()) {\n            return super.onTouchEvent(event);\n        }\n        final int action = event.getAction();\n\n        if (action != MotionEvent.ACTION_DOWN && (!mIsTouchDownInMoreText && mTouchSpanInfo == null)) {\n            return super.onTouchEvent(event);\n        }\n\n        // touch事件前先消耗掉还存在的mPendingPressCancelAction\n        if (mPendingPressCancelAction != null) {\n            mPendingPressCancelAction.run();\n            mPendingPressCancelAction = null;\n        }\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mTouchSpanInfo = null;\n                mIsTouchDownInMoreText = false;\n\n                if (mMoreHitRect.contains(x, y)) {\n                    mIsTouchDownInMoreText = true;\n                    invalidate(mMoreHitRect);\n                } else {\n\n                    for (SpanInfo spanInfo : mSpanInfos.values()) {\n                        if (spanInfo.onTouch(x, y)) {\n                            mTouchSpanInfo = spanInfo;\n                            break;\n                        }\n                    }\n                }\n\n                if (mTouchSpanInfo != null) {\n                    mTouchSpanInfo.setPressed(true);\n                    mTouchSpanInfo.invalidateSpan();\n                } else if (!mIsTouchDownInMoreText) {\n                    return super.onTouchEvent(event);\n                }\n\n                break;\n            case MotionEvent.ACTION_CANCEL:\n                mPendingPressCancelAction = null;\n                if (mTouchSpanInfo != null) {\n                    mTouchSpanInfo.setPressed(false);\n                    mTouchSpanInfo.invalidateSpan();\n                } else if (mIsTouchDownInMoreText) {\n                    mIsTouchDownInMoreText = false;\n                    invalidate(mMoreHitRect);\n                }\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mTouchSpanInfo != null && !mTouchSpanInfo.onTouch(x, y)) {\n                    mTouchSpanInfo.setPressed(false);\n                    mTouchSpanInfo.invalidateSpan();\n                    mTouchSpanInfo = null;\n                } else if (mIsTouchDownInMoreText && !mMoreHitRect.contains(x, y)) {\n                    mIsTouchDownInMoreText = false;\n                    invalidate(mMoreHitRect);\n                }\n                break;\n            case MotionEvent.ACTION_UP:\n                if (mTouchSpanInfo != null) {\n                    mTouchSpanInfo.onClick();\n                    mPendingPressCancelAction = new PressCancelAction(mTouchSpanInfo);\n                    postDelayed(new Runnable() {\n                        @Override\n                        public void run() {\n                            if (mPendingPressCancelAction != null) {\n                                mPendingPressCancelAction.run();\n                            }\n                        }\n                    }, 100);\n                } else if (mIsTouchDownInMoreText) {\n                    if (mListener != null) {\n                        mListener.onMoreTextClick();\n                    } else if (isClickable()) {\n                        performClick();\n                    }\n                    mIsTouchDownInMoreText = false;\n                    invalidate(mMoreHitRect);\n                }\n\n                break;\n        }\n        return true;\n    }\n\n    public void setCompiler(QMUIQQFaceCompiler compiler) {\n        if (mCompiler != compiler) {\n            mCompiler = compiler;\n            setText(mOriginText, false);\n        }\n    }\n\n    public void setTypeface(Typeface typeface) {\n        if (mTypeface != typeface) {\n            mTypeface = typeface;\n            needReCalculateFontHeight = true;\n            mPaint.setTypeface(typeface);\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setTypeface(Typeface tf, int style) {\n        if (style > 0) {\n            if (tf == null) {\n                tf = Typeface.defaultFromStyle(style);\n            } else {\n                tf = Typeface.create(tf, style);\n            }\n\n            setTypeface(tf);\n            // now compute what (if any) algorithmic styling is needed\n            int typefaceStyle = tf != null ? tf.getStyle() : 0;\n            int need = style & ~typefaceStyle;\n            mPaint.setFakeBoldText((need & Typeface.BOLD) != 0);\n            mPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);\n        } else {\n            mPaint.setFakeBoldText(false);\n            mPaint.setTextSkewX(0);\n            setTypeface(tf);\n        }\n    }\n\n    /**\n     * @param paragraphSpace only support for NO Ellipse or Ellipse End\n     */\n    public void setParagraphSpace(int paragraphSpace) {\n        if (mParagraphSpace != paragraphSpace) {\n            mParagraphSpace = paragraphSpace;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setMoreActionText(String moreActionText) {\n        if (mMoreActionText == null || !mMoreActionText.equals(moreActionText)) {\n            mMoreActionText = moreActionText;\n            measureMoreActionTextLength();\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setLinkUnderLineColor(int linkUnderLineColor) {\n        setLinkUnderLineColor(ColorStateList.valueOf(linkUnderLineColor));\n    }\n\n    public void setLinkUnderLineColor(ColorStateList linkUnderLineColor) {\n        if (mLinkUnderLineColor != linkUnderLineColor) {\n            mLinkUnderLineColor = linkUnderLineColor;\n            invalidate();\n        }\n    }\n\n    public void setLinkUnderLineHeight(int linkUnderLineHeight) {\n        if (mLinkUnderLineHeight != linkUnderLineHeight) {\n            mLinkUnderLineHeight = linkUnderLineHeight;\n            invalidate();\n        }\n    }\n\n    public void setNeedUnderlineForMoreText(boolean needUnderlineForMoreText) {\n        if (mIsNeedUnderlineForMoreText != needUnderlineForMoreText) {\n            mIsNeedUnderlineForMoreText = needUnderlineForMoreText;\n            invalidate();\n        }\n    }\n\n    public void setMoreActionColor(int color) {\n        setMoreActionColor(ColorStateList.valueOf(color));\n    }\n\n    public void setMoreActionColor(ColorStateList color) {\n        if (mMoreActionColor != color) {\n            mMoreActionColor = color;\n            invalidate();\n        }\n    }\n\n    public void setMoreActionBgColor(int color) {\n        setMoreActionBgColor(ColorStateList.valueOf(color));\n    }\n\n    public void setMoreActionBgColor(ColorStateList color) {\n        if (mMoreActionBgColor != color) {\n            mMoreActionBgColor = color;\n            invalidate();\n        }\n    }\n\n    private void measureMoreActionTextLength() {\n        if (QMUILangHelper.isNullOrEmpty(mMoreActionText)) {\n            mMoreActionTextLength = 0;\n        } else {\n            mMoreActionTextLength = (int) Math.ceil(mPaint.measureText(mMoreActionText));\n        }\n    }\n\n    public void setSpecialDrawablePadding(int specialDrawablePadding) {\n        if (mSpecialDrawablePadding != specialDrawablePadding) {\n            mSpecialDrawablePadding = specialDrawablePadding;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setIncludeFontPadding(boolean includePad) {\n        if (mIncludePad != includePad) {\n            needReCalculateFontHeight = true;\n            mIncludePad = includePad;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setQQFaceSizeAddon(int QQFaceSizeAddon) {\n        if (mQQFaceSizeAddon != QQFaceSizeAddon) {\n            mQQFaceSizeAddon = QQFaceSizeAddon;\n            mNeedReCalculateLines = true;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setLineSpace(int lineSpace) {\n        if (mLineSpace != lineSpace) {\n            mLineSpace = lineSpace;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setEllipsize(TextUtils.TruncateAt where) {\n        if (mEllipsize != where) {\n            mEllipsize = where;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setMaxLine(int maxLine) {\n        if (mMaxLine != maxLine) {\n            mMaxLine = maxLine;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public int getMaxLine() {\n        return mMaxLine;\n    }\n\n    public int getLineCount() {\n        return mLines;\n    }\n\n    public boolean isNeedEllipsize() {\n        return mIsNeedEllipsize;\n    }\n\n    public void setSingleLine(boolean singleLine) {\n        if (mIsSingleLine != singleLine) {\n            mIsSingleLine = singleLine;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setTextColor(@ColorInt int textColor) {\n        setTextColor(ColorStateList.valueOf(textColor));\n    }\n\n    public void setTextColor(ColorStateList textColor) {\n        if (mTextColor != textColor) {\n            mTextColor = textColor;\n            invalidate();\n        }\n    }\n\n    public TextPaint getPaint() {\n        return mPaint;\n    }\n\n    public void setTextSize(int textSize) {\n        if (mTextSize != textSize) {\n            mTextSize = textSize;\n            mPaint.setTextSize(mTextSize);\n            needReCalculateFontHeight = true;\n            mNeedReCalculateLines = true;\n            mEllipsizeTextLength = (int) Math.ceil(mPaint.measureText(mEllipsizeText));\n            measureMoreActionTextLength();\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public int getTextSize() {\n        return mTextSize;\n    }\n\n    public CharSequence getText() {\n        return mOriginText;\n    }\n\n    /**\n     * make sense only work after draw\n     *\n     * @return\n     */\n    public Rect getMoreHitRect() {\n        return mMoreHitRect;\n    }\n\n    public void setText(CharSequence charSequence) {\n        setText(charSequence, true);\n    }\n\n    private void setText(CharSequence charSequence, boolean compareOldText) {\n        if (compareOldText && QMUILangHelper.objectEquals(charSequence, mOriginText)) {\n            return;\n        }\n\n        mOriginText = charSequence;\n        setContentDescription(charSequence);\n        if (mOpenQQFace && mCompiler == null) {\n            throw new RuntimeException(\"mCompiler == null\");\n        }\n\n        mSpanInfos.clear();\n        if (QMUILangHelper.isNullOrEmpty(mOriginText)) {\n            mElementList = null;\n            requestLayout();\n            invalidate();\n            return;\n        }\n\n        if (mOpenQQFace && mCompiler != null) {\n            mElementList = mCompiler.compile(mOriginText);\n            List<QMUIQQFaceCompiler.Element> elements = mElementList.getElements();\n            if (elements != null) {\n                for (int i = 0; i < elements.size(); i++) {\n                    QMUIQQFaceCompiler.Element element = elements.get(i);\n                    if (element.getType() == QMUIQQFaceCompiler.ElementType.SPAN) {\n                        mSpanInfos.put(element, new SpanInfo(element.getTouchableSpan()));\n                    }\n                }\n            }\n        } else {\n            mElementList = new QMUIQQFaceCompiler.ElementList(0, mOriginText.length());\n            String[] strings = mOriginText.toString().split(\"\\\\n\");\n            for (int i = 0; i < strings.length; i++) {\n                mElementList.add(QMUIQQFaceCompiler.Element.createTextElement(strings[i]));\n                if (i != strings.length - 1) {\n                    mElementList.add(QMUIQQFaceCompiler.Element.createNextLineElement());\n                }\n            }\n        }\n        mNeedReCalculateLines = true;\n        if (getLayoutParams() == null) {\n            return;\n        }\n        if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT ||\n                getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {\n            requestLayout();\n            invalidate();\n            return;\n        }\n        int paddingHor = getPaddingLeft() + getPaddingRight();\n        int paddingVer = getPaddingBottom() + getPaddingTop();\n        if (getWidth() > paddingHor && getHeight() > paddingVer) {\n            mLines = 0;\n            calculateLinesAndContentWidth(getWidth());\n            int oldDrawLine = mNeedDrawLine;\n            int maxLine = Math.min((getHeight() - paddingVer + mLineSpace) / (mFontHeight + mLineSpace), mMaxLine);\n            calculateNeedDrawLine(maxLine);\n            // 优化： 如果高度固定或者绘制的行数相同，则不进行requestLayout\n            if (oldDrawLine == mNeedDrawLine) {\n                invalidate();\n            } else {\n                requestLayout();\n                invalidate();\n            }\n        }\n    }\n\n    private boolean needReCalculateFontHeight = true;\n\n    protected int calculateFontHeight() {\n        if (needReCalculateFontHeight) {\n            Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();\n            if (fontMetricsInt == null) {\n                mFontHeight = mQQFaceSize = 0;\n            } else {\n                needReCalculateFontHeight = false;\n                int top = getFontHeightCalTop(fontMetricsInt, mIncludePad);\n                int bottom = getFontHeightCalBottom(fontMetricsInt, mIncludePad);\n                int fontHeight = bottom - top;\n                mQQFaceSize = fontHeight + mQQFaceSizeAddon;\n                int specialMaxDrawableHeight = mCompiler.getSpecialBoundsMaxHeight();\n                int drawableSize = Math.max(mQQFaceSize, specialMaxDrawableHeight);\n                if (fontHeight >= drawableSize) {\n                    mFontHeight = fontHeight;\n                    mFirstBaseLine = -top;\n                } else {\n                    mFontHeight = drawableSize;\n                    mFirstBaseLine = -top + (drawableSize - fontHeight) / 2;\n                }\n            }\n        }\n        return mFontHeight;\n    }\n\n    public int getFontHeight() {\n        return mFontHeight;\n    }\n\n    public int getLineSpace() {\n        return mLineSpace;\n    }\n\n    protected int getFontHeightCalTop(Paint.FontMetricsInt fontMetricsInt, boolean includePad) {\n        return includePad ? fontMetricsInt.top : fontMetricsInt.ascent;\n    }\n\n    protected int getFontHeightCalBottom(Paint.FontMetricsInt fontMetricsInt, boolean includePad) {\n        return includePad ? fontMetricsInt.bottom : fontMetricsInt.descent;\n    }\n\n    @Override\n    public void setPadding(int left, int top, int right, int bottom) {\n        if (getPaddingLeft() != left || getPaddingRight() != right) {\n            mNeedReCalculateLines = true;\n        }\n        super.setPadding(left, top, right, bottom);\n    }\n\n    private int mCurrentCalWidth = 0;\n    private int mCurrentCalLine = 0;\n    private int mContentCalMaxWidth = 0;\n    private boolean mNeedReCalculateLines = false; // 缓存，避免onMeasure重复计算\n    private int mLastCalLimitWidth = 0;\n    private int mLastCalContentWidth = 0;\n    private int mLastCalLines = 0;\n\n    protected int calculateLinesAndContentWidth(int limitWidth) {\n        if (limitWidth <= (getPaddingRight() + getPaddingLeft()) || isElementEmpty()) {\n            mLines = 0;\n            mParagraphShowCount = 0;\n            mLastCalLines = 0;\n            mLastCalContentWidth = 0;\n            return mLastCalContentWidth;\n        }\n\n        if (!mNeedReCalculateLines && mLastCalLimitWidth == limitWidth) {\n            mLines = mLastCalLines;\n            return mLastCalContentWidth;\n        }\n        mLastCalLimitWidth = limitWidth;\n        List<QMUIQQFaceCompiler.Element> elements = mElementList.getElements();\n        mCurrentCalLine = 1;\n        mCurrentCalWidth = getPaddingLeft();\n        calculateLinesInner(elements, limitWidth);\n        if (mCurrentCalLine != mLines) {\n            if (mListener != null) {\n                mListener.onCalculateLinesChange(mCurrentCalLine);\n            }\n            mLines = mCurrentCalLine;\n        }\n\n        if (mLines == 1) {\n            mLastCalContentWidth = mCurrentCalWidth + getPaddingRight();\n        } else {\n            mLastCalContentWidth = limitWidth;\n        }\n        mLastCalLines = mLines;\n\n        return mLastCalContentWidth;\n    }\n\n    private void calculateNeedDrawLine(int maxline) {\n        mNeedDrawLine = mLines;\n        if (mIsSingleLine) {\n            mNeedDrawLine = Math.min(1, mLines);\n        } else if (maxline < mLines) {\n            mNeedDrawLine = maxline;\n        }\n\n        mIsNeedEllipsize = mLines > mNeedDrawLine;\n    }\n\n    private void calculateLinesInner(List<QMUIQQFaceCompiler.Element> elements, int limitWidth) {\n        QMUIQQFaceCompiler.Element element;\n        int widthStart = getPaddingLeft(), widthEnd = limitWidth - getPaddingRight();\n        for (int i = 0; i < elements.size(); i++) {\n            if (mJumpHandleMeasureAndDraw) {\n                break;\n            }\n            if (mCurrentCalLine > mMaxLine && mEllipsize == TextUtils.TruncateAt.END) {\n                break;\n            }\n            element = elements.get(i);\n            if (element.getType() == QMUIQQFaceCompiler.ElementType.DRAWABLE) {\n                if (mCurrentCalWidth + mQQFaceSize > widthEnd) {\n                    gotoCalNextLine(widthStart);\n                }\n                mCurrentCalWidth += mQQFaceSize;\n                if (widthEnd - widthStart < mQQFaceSize) {\n                    // 一个表情的宽度都容不下\n                    mJumpHandleMeasureAndDraw = true;\n                }\n            } else if (element.getType() == QMUIQQFaceCompiler.ElementType.TEXT) {\n                CharSequence text = element.getText();\n                measureText(text, widthStart, widthEnd);\n            } else if (element.getType() == QMUIQQFaceCompiler.ElementType.SPAN) {\n                QMUIQQFaceCompiler.ElementList spanElementList = element.getChildList();\n                ITouchableSpan span = element.getTouchableSpan();\n                if (spanElementList != null && spanElementList.getElements().size() > 0) {\n                    if (span == null) {\n                        calculateLinesInner(spanElementList.getElements(), limitWidth);\n                        continue;\n                    }\n                    calculateLinesInner(spanElementList.getElements(), limitWidth);\n                }\n            } else if (element.getType() == QMUIQQFaceCompiler.ElementType.NEXTLINE) {\n                gotoCalNextLine(widthStart, true);\n            } else if (element.getType() == QMUIQQFaceCompiler.ElementType.SPECIAL_BOUNDS_DRAWABLE) {\n                Drawable drawable = element.getSpecialBoundsDrawable();\n                int width = drawable.getIntrinsicWidth();\n                if (i == 0 || i == elements.size() - 1) {\n                    width += mSpecialDrawablePadding;\n                } else {\n                    width += mSpecialDrawablePadding * 2;\n                }\n                if (mCurrentCalWidth + width > widthEnd) {\n                    gotoCalNextLine(widthStart);\n                    mCurrentCalWidth += width;\n                } else if (mCurrentCalWidth + width == widthEnd) {\n                    gotoCalNextLine(widthStart);\n                } else {\n                    mCurrentCalWidth += width;\n                }\n                if (widthEnd - widthStart < width) {\n                    // 一个表情的宽度都容不下\n                    mJumpHandleMeasureAndDraw = true;\n                }\n            }\n        }\n    }\n\n    private boolean isElementEmpty() {\n        return mElementList == null ||\n                mElementList.getElements() == null ||\n                mElementList.getElements().isEmpty();\n    }\n\n    private void setContentCalMaxWidth(int width) {\n        mContentCalMaxWidth = Math.max(width, mContentCalMaxWidth);\n    }\n\n    private void gotoCalNextLine(int widthStart) {\n        gotoCalNextLine(widthStart, false);\n    }\n\n    private void gotoCalNextLine(int widthStart, boolean nextParagraph) {\n        mCurrentCalLine++;\n        setContentCalMaxWidth(mCurrentCalWidth);\n        mCurrentCalWidth = widthStart;\n        if (nextParagraph) {\n            if (mEllipsize == null) {\n                mParagraphShowCount++;\n            } else if (mEllipsize == TextUtils.TruncateAt.END) {\n                if (mCurrentCalLine <= mMaxLine) {\n                    mParagraphShowCount++;\n                }\n            }\n        }\n    }\n\n    private void measureText(CharSequence text, int widthStart, int widthEnd) {\n\n        float[] widths = new float[text.length()];\n        mPaint.getTextWidths(text.toString(), widths);\n        int contentWidth = widthEnd - widthStart;\n        long loop_start = System.currentTimeMillis();\n        for (int i = 0; i < widths.length; i++) {\n            if (contentWidth < widths[i]) {\n                // mCurrentCalWidth已经是最小值，但又一个字都容纳不下，只能说明widthEnd太小，可能还在测量中\n                mJumpHandleMeasureAndDraw = true;\n                return;\n            }\n            if (System.currentTimeMillis() - loop_start > 2000) {\n                // 3s还没有measure完，那就忽略本次measure以及draw\n                QMUILog.d(TAG, \"measureText: text = %s, mCurrentCalWidth = %d, \" +\n                                \"widthStart = %d, widthEnd = %d\",\n                        text, mCurrentCalWidth, widthStart, widthEnd);\n                mJumpHandleMeasureAndDraw = true;\n                break;\n            }\n            if (mCurrentCalWidth + widths[i] > widthEnd) {\n                gotoCalNextLine(widthStart);\n            }\n            mCurrentCalWidth += Math.ceil(widths[i]);\n        }\n    }\n\n    public void setListener(QQFaceViewListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {\n        super.onInitializeAccessibilityNodeInfo(info);\n        info.setText(getText());\n        info.setContentDescription(getText());\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        long start = System.currentTimeMillis();\n        mJumpHandleMeasureAndDraw = false;\n        calculateFontHeight();\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\n        mLines = 0;\n        mParagraphShowCount = 0;\n        int width, height;\n        switch (widthMode) {\n            case AT_MOST:\n            default:\n                if (mOriginText == null || mOriginText.length() == 0) {\n                    width = 0;\n                } else {\n                    width = calculateLinesAndContentWidth(Math.min(widthSize, mMaxWidth));\n                }\n                break;\n            case MeasureSpec.EXACTLY:\n            case MeasureSpec.UNSPECIFIED:\n                width = widthSize;\n                calculateLinesAndContentWidth(width);\n        }\n\n        if (mJumpHandleMeasureAndDraw) {\n            setMeasuredDimension(width, heightMode == AT_MOST ? 0 : heightSize);\n            return;\n        }\n\n        int maxLine = mMaxLine;\n\n        switch (heightMode) {\n            case AT_MOST:\n                // calculate line count first\n                maxLine = (heightSize - getPaddingTop() - getPaddingBottom() + mLineSpace) / (mFontHeight + mLineSpace);\n                maxLine = Math.min(maxLine, mMaxLine);\n                calculateNeedDrawLine(maxLine);\n                height = getPaddingTop() + getPaddingBottom();\n                if (mNeedDrawLine < 2) {\n                    height += mNeedDrawLine * mFontHeight;\n                } else {\n                    height += (mNeedDrawLine - 1) * (mFontHeight + mLineSpace) + mFontHeight + mParagraphShowCount * mParagraphSpace;\n                }\n                break;\n            case MeasureSpec.UNSPECIFIED:\n            default:\n                // calculate line count first\n                calculateNeedDrawLine(mMaxLine);\n                height = getPaddingTop() + getPaddingBottom();\n                if (mNeedDrawLine < 2) {\n                    height += mNeedDrawLine * mFontHeight;\n                } else {\n                    height += (mNeedDrawLine - 1) * (mFontHeight + mLineSpace) + mFontHeight + mParagraphShowCount * mParagraphSpace;\n                }\n                break;\n            case MeasureSpec.EXACTLY:\n                height = heightSize;\n                maxLine = (height - getPaddingTop() - getPaddingBottom() + mLineSpace) / (mFontHeight + mLineSpace);\n                maxLine = Math.min(maxLine, mMaxLine);\n                calculateNeedDrawLine(maxLine);\n                break;\n        }\n        setMeasuredDimension(width, height);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        if (mJumpHandleMeasureAndDraw || mOriginText == null || mLines == 0 || isElementEmpty()) {\n            return;\n        }\n        pickTextPaintColor();\n        List<QMUIQQFaceCompiler.Element> elements = mElementList.getElements();\n        mCurrentDrawBaseLine = getPaddingTop() + mFirstBaseLine;\n        mCurrentDrawLine = 1;\n        setStartDrawUsedWidth(getPaddingLeft(), getWidth() - getPaddingLeft() - getPaddingRight());\n        mIsExecutedMiddleEllipsize = false;\n        drawElements(canvas, elements, getWidth() - getPaddingLeft() - getPaddingRight());\n    }\n\n    private void pickTextPaintColor() {\n        if (mTextColor != null) {\n            int defaultColor = mTextColor.getDefaultColor();\n            if (isPressed()) {\n                mPaint.setColor(mTextColor.getColorForState(mPressedState, defaultColor));\n            } else {\n                mPaint.setColor(defaultColor);\n            }\n        }\n    }\n\n\n    private int mCurrentDrawBaseLine;\n    private int mCurrentDrawLine;\n    private int mCurrentDrawUsedWidth;\n    private boolean mIsInDrawSpan = false;\n    private QMUITouchableSpan mCurrentDrawSpan;\n\n    private void drawElements(Canvas canvas, List<QMUIQQFaceCompiler.Element> elements, int usefulWidth) {\n        int startLeft = getPaddingLeft(), endWidth = usefulWidth + startLeft;\n        if (mIsNeedEllipsize && mEllipsize == TextUtils.TruncateAt.START) {\n            canvas.drawText(mEllipsizeText, 0, mEllipsizeText.length(), startLeft, mFirstBaseLine, mPaint);\n        }\n\n        QMUIQQFaceCompiler.Element element;\n        for (int i = 0; i < elements.size(); i++) {\n            element = elements.get(i);\n            QMUIQQFaceCompiler.ElementType type = element.getType();\n            if (type == QMUIQQFaceCompiler.ElementType.DRAWABLE) {\n                onDrawQQFace(canvas, element.getDrawableRes(), null, startLeft, endWidth, i == 0, i == elements.size() - 1);\n            } else if (type == QMUIQQFaceCompiler.ElementType.SPECIAL_BOUNDS_DRAWABLE) {\n                onDrawQQFace(canvas, 0, element.getSpecialBoundsDrawable(), startLeft, endWidth, i == 0, i == elements.size() - 1);\n            } else if (type == QMUIQQFaceCompiler.ElementType.TEXT) {\n                CharSequence text = element.getText();\n                float[] fontWidths = new float[text.length()];\n                mPaint.getTextWidths(text.toString(), fontWidths);\n                onDrawText(canvas, text, fontWidths, 0, startLeft, endWidth);\n            } else if (type == QMUIQQFaceCompiler.ElementType.SPAN) {\n                QMUIQQFaceCompiler.ElementList spanElementList = element.getChildList();\n                mCurrentDrawSpan = element.getTouchableSpan();\n                SpanInfo spanInfo = mSpanInfos.get(element);\n                if (spanElementList != null && !spanElementList.getElements().isEmpty()) {\n                    if (mCurrentDrawSpan == null) {\n                        drawElements(canvas, spanElementList.getElements(), usefulWidth);\n                        continue;\n                    }\n                    mIsInDrawSpan = true;\n                    if (spanInfo != null) {\n                        spanInfo.setStart(mCurrentDrawLine, mCurrentDrawUsedWidth);\n                    }\n                    @ColorInt int spanColor = mCurrentDrawSpan.isPressed() ?\n                            mCurrentDrawSpan.getPressedTextColor() :\n                            mCurrentDrawSpan.getNormalTextColor();\n                    if (spanColor == 0) {\n                        pickTextPaintColor();\n                    } else {\n                        mPaint.setColor(spanColor);\n                    }\n                    drawElements(canvas, spanElementList.getElements(), usefulWidth);\n                    pickTextPaintColor();\n                    if (spanInfo != null) {\n                        spanInfo.setEnd(mCurrentDrawLine, mCurrentDrawUsedWidth);\n                    }\n                    mIsInDrawSpan = false;\n                }\n            } else if (type == QMUIQQFaceCompiler.ElementType.NEXTLINE) {\n                int ellipsizeLength = mEllipsizeTextLength + mMoreActionTextLength;\n                if (mIsNeedEllipsize && mEllipsize == TextUtils.TruncateAt.END &&\n                        mCurrentDrawUsedWidth <= endWidth - ellipsizeLength && mCurrentDrawLine == mNeedDrawLine) {\n                    drawText(canvas, mEllipsizeText, 0, mEllipsizeText.length(), mEllipsizeTextLength);\n                    mCurrentDrawUsedWidth += mEllipsizeTextLength;\n                    drawMoreActionText(canvas, endWidth);\n                    return;\n                }\n                toNewDrawLine(startLeft, true, usefulWidth);\n            }\n        }\n    }\n\n    private void drawMoreActionText(Canvas canvas, int widthEnd) {\n        if (!QMUILangHelper.isNullOrEmpty(mMoreActionText)) {\n            ColorStateList colorStateList = mMoreActionColor == null ? mTextColor : mMoreActionColor;\n            int bgColor = 0;\n            int color = 0;\n            if (colorStateList != null) {\n                color = colorStateList.getDefaultColor();\n                if (mIsTouchDownInMoreText) {\n                    color = colorStateList.getColorForState(mPressedState, color);\n                }\n            }\n            if (mMoreActionBgColor != null) {\n                bgColor = mMoreActionBgColor.getDefaultColor();\n                if (mIsTouchDownInMoreText) {\n                    bgColor = mMoreActionBgColor.getColorForState(mPressedState, bgColor);\n                }\n            }\n            int top = getPaddingTop();\n            if (mCurrentDrawLine > 1) {\n                top = (mCurrentDrawLine - 1) * (mFontHeight + mLineSpace) + top;\n            }\n            mMoreHitRect.set(mCurrentDrawUsedWidth, top, mCurrentDrawUsedWidth + mMoreActionTextLength, top + mFontHeight);\n\n            if (bgColor != 0) {\n                mDecorationPaint.setColor(bgColor);\n                mDecorationPaint.setStyle(Paint.Style.FILL);\n                canvas.drawRect(mMoreHitRect, mDecorationPaint);\n            }\n            mPaint.setColor(color);\n            canvas.drawText(mMoreActionText, 0, mMoreActionText.length(),\n                    mCurrentDrawUsedWidth, mCurrentDrawBaseLine, mPaint);\n\n            if (mIsNeedUnderlineForMoreText && mLinkUnderLineHeight > 0) {\n                ColorStateList underLineColors = mLinkUnderLineColor == null ? mTextColor : mLinkUnderLineColor;\n                if (underLineColors != null) {\n                    int underLineColor = underLineColors.getDefaultColor();\n                    if (mIsTouchDownInMoreText) {\n                        underLineColor = underLineColors.getColorForState(mPressedState, underLineColor);\n                    }\n                    mDecorationPaint.setColor(underLineColor);\n                    mDecorationPaint.setStyle(Paint.Style.STROKE);\n                    mDecorationPaint.setStrokeWidth(mLinkUnderLineHeight);\n                    canvas.drawLine(mMoreHitRect.left, mMoreHitRect.bottom,\n                            mMoreHitRect.right, mMoreHitRect.bottom, mDecorationPaint);\n                }\n\n            }\n            pickTextPaintColor();\n        }\n    }\n\n    private void toNewDrawLine(int startLeft, int usefulWidth) {\n        toNewDrawLine(startLeft, false, usefulWidth);\n    }\n\n    /**\n     * control for paragraph space if mEllipsize == null || mEllipsize == TextUtils.TruncateAt.END\n     */\n    private void toNewDrawLine(int startLeft, boolean paragraph, int usefulWidth) {\n        int addOn = (paragraph && (mEllipsize == null || mEllipsize == TextUtils.TruncateAt.END) ? mParagraphSpace : 0) + mLineSpace;\n        mCurrentDrawLine++;\n        if (mIsNeedEllipsize) {\n            if (mEllipsize == TextUtils.TruncateAt.START) {\n                if (mCurrentDrawLine > mLines - mNeedDrawLine + 1) {\n                    mCurrentDrawBaseLine += mFontHeight + addOn;\n                }\n            } else if (mEllipsize == TextUtils.TruncateAt.MIDDLE) {\n                if (!mIsExecutedMiddleEllipsize || mMiddleEllipsizeWidthRecord == -1) {\n                    mCurrentDrawBaseLine += mFontHeight + addOn;\n                }\n            } else {\n                mCurrentDrawBaseLine += mFontHeight + addOn;\n            }\n            if (mEllipsize != null && mEllipsize != TextUtils.TruncateAt.END && mCurrentDrawBaseLine > getHeight() - getPaddingBottom()) {\n                QMUILog.d(TAG, \"draw outside the visible height, the ellipsize is inaccurate: \" +\n                                \"mEllipsize = %s; mCurrentDrawLine = %d; mNeedDrawLine = %d;\" +\n                                \"viewWidth = %d; viewHeight = %d; paddingLeft = %d; \" +\n                                \"paddingRight = %d; paddingTop = %d; paddingBottom = %d; text = %s\",\n                        mEllipsize.name(), mCurrentDrawLine, mNeedDrawLine,\n                        getWidth(), getHeight(), getPaddingLeft(), getPaddingRight(),\n                        getPaddingTop(), getPaddingBottom(), mOriginText);\n            }\n        } else {\n            mCurrentDrawBaseLine += mFontHeight + addOn;\n        }\n        setStartDrawUsedWidth(startLeft, usefulWidth);\n    }\n\n    private void setStartDrawUsedWidth(int startLeft, int usefulWidth) {\n        if (mIsNeedEllipsize) {\n            mCurrentDrawUsedWidth = startLeft;\n            return;\n        }\n        if (mCurrentDrawLine == mNeedDrawLine) {\n            if (mGravity == Gravity.CENTER) {\n                mCurrentDrawUsedWidth = (usefulWidth - (mCurrentCalWidth - startLeft)) / 2 + startLeft;\n            } else if (mGravity == Gravity.RIGHT) {\n                mCurrentDrawUsedWidth = (usefulWidth - (mCurrentCalWidth - startLeft)) + startLeft;\n            } else {\n                mCurrentDrawUsedWidth = startLeft;\n            }\n        } else {\n            mCurrentDrawUsedWidth = startLeft;\n        }\n    }\n\n    private void onRealDrawText(Canvas canvas, CharSequence text, float[] fontWidths, int offset, int widthStart, int widthEnd) {\n        int startPos = offset;\n        int targetUsedWidth = mCurrentDrawUsedWidth;\n        for (int i = offset; i < fontWidths.length; i++) {\n            if (targetUsedWidth + fontWidths[i] > widthEnd) {\n                drawText(canvas, text, startPos, i, widthEnd - mCurrentDrawUsedWidth);\n                toNewDrawLine(widthStart, widthEnd - widthStart);\n                targetUsedWidth = mCurrentDrawUsedWidth;\n                startPos = i;\n            }\n            targetUsedWidth += fontWidths[i];\n        }\n        if (startPos < fontWidths.length) {\n            drawText(canvas, text, startPos, fontWidths.length, targetUsedWidth - mCurrentDrawUsedWidth);\n            mCurrentDrawUsedWidth = targetUsedWidth;\n        }\n    }\n\n    private int getMiddleEllipsizeLine() {\n        int ellipsizeLine;\n        if (mNeedDrawLine % 2 == 0) {\n            ellipsizeLine = mNeedDrawLine / 2;\n        } else {\n            ellipsizeLine = (mNeedDrawLine + 1) / 2;\n        }\n        return ellipsizeLine;\n    }\n\n\n    private int mMiddleEllipsizeWidthRecord = -1;\n    private boolean mIsExecutedMiddleEllipsize = false;\n\n    private void onDrawText(Canvas canvas, CharSequence text, float[] fontWidths, int offset, int widthStart, int widthEnd) {\n        if (offset >= text.length()) {\n            return;\n        }\n        if (mIsNeedEllipsize) {\n            if (mEllipsize == TextUtils.TruncateAt.START) {\n                if (mCurrentDrawLine > mLines - mNeedDrawLine) {\n                    onRealDrawText(canvas, text, fontWidths, offset, widthStart, widthEnd);\n                } else if (mCurrentDrawLine < mLines - mNeedDrawLine) {\n                    for (int i = offset; i < text.length(); i++) {\n                        if (mCurrentDrawUsedWidth + fontWidths[i] <= widthEnd) {\n                            mCurrentDrawUsedWidth += fontWidths[i];\n                        } else {\n                            toNewDrawLine(widthStart, widthEnd - widthStart);\n                            onDrawText(canvas, text, fontWidths, i, widthStart, widthEnd);\n                            return;\n                        }\n                    }\n                } else {\n                    int needStopWidth = mCurrentCalWidth + mEllipsizeTextLength;\n                    for (int i = offset; i < text.length(); i++) {\n                        if (mCurrentDrawUsedWidth + fontWidths[i] <= needStopWidth) {\n                            mCurrentDrawUsedWidth += fontWidths[i];\n                        } else {\n                            int newStart = i + 1;\n                            if (mCurrentDrawUsedWidth > needStopWidth) {\n                                newStart = i;\n                            }\n                            toNewDrawLine(widthStart + mEllipsizeTextLength, widthEnd - widthStart);\n                            onDrawText(canvas, text, fontWidths, newStart, widthStart, widthEnd);\n                            return;\n                        }\n                    }\n                }\n            } else if (mEllipsize == TextUtils.TruncateAt.MIDDLE) {\n                int ellipsizeLine = getMiddleEllipsizeLine();\n                if (mCurrentDrawLine < ellipsizeLine) {\n                    int targetDrawWidth = mCurrentDrawUsedWidth;\n                    for (int i = offset; i < fontWidths.length; i++) {\n                        if (targetDrawWidth + fontWidths[i] <= widthEnd) {\n                            targetDrawWidth += fontWidths[i];\n                        } else {\n                            drawText(canvas, text, offset, i, widthEnd - mCurrentDrawUsedWidth);\n                            toNewDrawLine(widthStart, widthEnd - widthStart);\n                            onDrawText(canvas, text, fontWidths, i, widthStart, widthEnd);\n                            return;\n                        }\n                    }\n                    drawText(canvas, text, offset, text.length(), targetDrawWidth - mCurrentDrawUsedWidth);\n                    mCurrentDrawUsedWidth = targetDrawWidth;\n                } else if (mCurrentDrawLine == ellipsizeLine) {\n                    if (mIsExecutedMiddleEllipsize) {\n                        handleTextAfterMiddleEllipsize(canvas, text, fontWidths, offset,\n                                ellipsizeLine, widthStart, widthEnd);\n                    } else {\n                        int needStop = (widthEnd + widthStart) / 2 - mEllipsizeTextLength / 2;\n                        int targetDrawWidth = mCurrentDrawUsedWidth;\n                        for (int i = offset; i < fontWidths.length; i++) {\n                            if (targetDrawWidth + fontWidths[i] <= needStop) {\n                                targetDrawWidth += fontWidths[i];\n                            } else {\n                                drawText(canvas, text, offset, i, targetDrawWidth - mCurrentDrawUsedWidth);\n                                mCurrentDrawUsedWidth = targetDrawWidth;\n                                drawText(canvas, mEllipsizeText, 0, mEllipsizeText.length(), mEllipsizeTextLength);\n                                mMiddleEllipsizeWidthRecord = mCurrentDrawUsedWidth + mEllipsizeTextLength;\n                                mIsExecutedMiddleEllipsize = true;\n                                handleTextAfterMiddleEllipsize(canvas, text, fontWidths, i,\n                                        ellipsizeLine, widthStart, widthEnd);\n                                return;\n                            }\n                        }\n                        drawText(canvas, text, offset, text.length(), targetDrawWidth - mCurrentDrawUsedWidth);\n                        mCurrentDrawUsedWidth = targetDrawWidth;\n                    }\n                } else {\n                    handleTextAfterMiddleEllipsize(canvas, text, fontWidths, offset,\n                            ellipsizeLine, widthStart, widthEnd);\n                }\n            } else {\n                if (mCurrentDrawLine < mNeedDrawLine) {\n                    int targetUsedWidth = mCurrentDrawUsedWidth;\n                    for (int i = offset; i < fontWidths.length; i++) {\n                        if (targetUsedWidth + fontWidths[i] <= widthEnd) {\n                            targetUsedWidth += fontWidths[i];\n                        } else {\n                            drawText(canvas, text, offset, i, widthEnd - mCurrentDrawUsedWidth);\n                            toNewDrawLine(widthStart, widthEnd - widthStart);\n                            onDrawText(canvas, text, fontWidths, i, widthStart, widthEnd);\n                            return;\n                        }\n                    }\n                    drawText(canvas, text, offset, fontWidths.length, targetUsedWidth - mCurrentDrawUsedWidth);\n                    mCurrentDrawUsedWidth = targetUsedWidth;\n                } else if (mCurrentDrawLine == mNeedDrawLine) {\n                    int ellipsizeLength = mMoreActionTextLength;\n                    if (mEllipsize == TextUtils.TruncateAt.END) {\n                        ellipsizeLength += mEllipsizeTextLength;\n                    }\n\n                    int targetUsedWidth = mCurrentDrawUsedWidth;\n                    for (int i = offset; i < fontWidths.length; i++) {\n                        if (targetUsedWidth + fontWidths[i] <= widthEnd - ellipsizeLength) {\n                            targetUsedWidth += fontWidths[i];\n                        } else {\n                            drawText(canvas, text, offset, i, targetUsedWidth - mCurrentDrawUsedWidth);\n                            mCurrentDrawUsedWidth = targetUsedWidth;\n                            if (mEllipsize == TextUtils.TruncateAt.END) {\n                                drawText(canvas, mEllipsizeText, 0, mEllipsizeText.length(), mEllipsizeTextLength);\n                                mCurrentDrawUsedWidth += mEllipsizeTextLength;\n                            }\n                            drawMoreActionText(canvas, widthEnd);\n                            // 依然要去到下一行，使得后续不会进入这个逻辑\n                            toNewDrawLine(widthStart, widthEnd - widthStart);\n                            return;\n                        }\n                    }\n                    drawText(canvas, text, offset, fontWidths.length, targetUsedWidth - mCurrentDrawUsedWidth);\n                    mCurrentDrawUsedWidth = targetUsedWidth;\n                }\n            }\n\n        } else {\n            onRealDrawText(canvas, text, fontWidths, 0, widthStart, widthEnd);\n        }\n    }\n\n    private void handleTextAfterMiddleEllipsize(Canvas canvas, CharSequence text, float[] fontWidths,\n                                                int offset, int ellipsizeLine, int widthStart, int widthEnd) {\n        if (offset >= text.length()) {\n            return;\n        }\n        if (mMiddleEllipsizeWidthRecord == -1) {\n            onRealDrawText(canvas, text, fontWidths, offset, widthStart, widthEnd);\n            return;\n        }\n        int endLines = mNeedDrawLine - ellipsizeLine;\n        int borrowWidth = widthEnd - mCurrentCalWidth - (mMiddleEllipsizeWidthRecord - widthStart);\n        int needStopLine = borrowWidth > 0 ? mLines - endLines - 1 : mLines - endLines;\n        int needStopWidth = borrowWidth > 0 ? widthEnd - borrowWidth :\n                mMiddleEllipsizeWidthRecord - (widthEnd - mCurrentCalWidth);\n\n\n        if (mCurrentDrawLine < needStopLine) {\n            for (int i = offset; i < fontWidths.length; i++) {\n                if (mCurrentDrawUsedWidth + fontWidths[i] <= widthEnd) {\n                    mCurrentDrawUsedWidth += fontWidths[i];\n                } else {\n                    toNewDrawLine(widthStart, widthStart - widthEnd);\n                    handleTextAfterMiddleEllipsize(canvas, text, fontWidths, i, ellipsizeLine, widthStart, widthEnd);\n                    return;\n                }\n            }\n        } else if (mCurrentDrawLine == needStopLine) {\n            for (int i = offset; i < fontWidths.length; i++) {\n                if (mCurrentDrawUsedWidth + fontWidths[i] <= needStopWidth) {\n                    mCurrentDrawUsedWidth += fontWidths[i];\n                } else {\n                    int newStart = i + 1;\n                    if (mCurrentDrawUsedWidth >= needStopWidth) {\n                        newStart = i;\n                    }\n                    mCurrentDrawUsedWidth = mMiddleEllipsizeWidthRecord;\n                    mMiddleEllipsizeWidthRecord = -1;\n                    mLastNeedStopLineRecord = needStopLine;\n                    onRealDrawText(canvas, text, fontWidths, newStart, widthStart, widthEnd);\n                    return;\n                }\n            }\n        } else {\n            onRealDrawText(canvas, text, fontWidths, offset, widthStart, widthEnd);\n        }\n    }\n\n    private void drawText(Canvas canvas, CharSequence text, int start, int end, int textWidth) {\n        if (end <= start || end > text.length() || start >= text.length()) {\n            return;\n        }\n        if (mIsInDrawSpan && mCurrentDrawSpan != null) {\n            @ColorInt int color = mCurrentDrawSpan.isPressed() ? mCurrentDrawSpan.getPressedBackgroundColor() :\n                    mCurrentDrawSpan.getNormalBackgroundColor();\n            if (color != Color.TRANSPARENT) {\n                mDecorationPaint.setColor(color);\n                mDecorationPaint.setStyle(Paint.Style.FILL);\n                canvas.drawRect(mCurrentDrawUsedWidth, mCurrentDrawBaseLine - mFirstBaseLine,\n                        mCurrentDrawUsedWidth + textWidth,\n                        mCurrentDrawBaseLine - mFirstBaseLine + mFontHeight, mDecorationPaint);\n            }\n        }\n        canvas.drawText(text, start, end, mCurrentDrawUsedWidth, mCurrentDrawBaseLine, mPaint);\n\n        if (mIsInDrawSpan && mCurrentDrawSpan != null &&\n                mCurrentDrawSpan.isNeedUnderline() && mLinkUnderLineHeight > 0) {\n            ColorStateList underLineColors = mLinkUnderLineColor == null ? mTextColor : mLinkUnderLineColor;\n            if (underLineColors != null) {\n                int underLineColor = underLineColors.getDefaultColor();\n                if (mCurrentDrawSpan.isPressed()) {\n                    underLineColor = underLineColors.getColorForState(mPressedState, underLineColor);\n                }\n                mDecorationPaint.setColor(underLineColor);\n                mDecorationPaint.setStyle(Paint.Style.STROKE);\n                mDecorationPaint.setStrokeWidth(mLinkUnderLineHeight);\n                int bottom = mCurrentDrawBaseLine - mFirstBaseLine + mFontHeight;\n                canvas.drawLine(mCurrentDrawUsedWidth, bottom, mCurrentDrawUsedWidth + textWidth,\n                        bottom, mDecorationPaint);\n            }\n        }\n    }\n\n    private void onDrawQQFace(Canvas canvas, int res, @Nullable Drawable specialDrawable, int widthStart, int widthEnd, boolean isFirst, boolean isLast) {\n        int size = res != 0 || specialDrawable == null ? mQQFaceSize : specialDrawable.getIntrinsicWidth() + (isFirst || isLast ? mSpecialDrawablePadding : mSpecialDrawablePadding * 2);\n        if (mIsNeedEllipsize) {\n            if (mEllipsize == TextUtils.TruncateAt.START) {\n                if (mCurrentDrawLine > mLines - mNeedDrawLine) {\n                    onRealDrawQQFace(canvas, res, specialDrawable, mNeedDrawLine - mLines, widthStart, widthEnd, isFirst, isLast);\n                } else if (mCurrentDrawLine < mLines - mNeedDrawLine) {\n                    if (size + mCurrentDrawUsedWidth > widthEnd) {\n                        toNewDrawLine(widthStart, widthEnd - widthStart);\n                        onDrawQQFace(canvas, res, specialDrawable, widthStart, widthEnd, isFirst, isLast);\n                    } else {\n                        mCurrentDrawUsedWidth += size;\n                    }\n                } else {\n                    int needStopWidth = mCurrentCalWidth + mEllipsizeTextLength;\n                    if (size + mCurrentDrawUsedWidth < needStopWidth) {\n                        mCurrentDrawUsedWidth += size;\n                    } else {\n                        toNewDrawLine(widthStart + mEllipsizeTextLength, widthEnd - widthStart);\n                    }\n                }\n            } else if (mEllipsize == TextUtils.TruncateAt.MIDDLE) {\n                int ellipsizeLine = getMiddleEllipsizeLine();\n                if (mCurrentDrawLine < ellipsizeLine) {\n                    if (size + mCurrentDrawUsedWidth > widthEnd) {\n                        onRealDrawQQFace(canvas, res, specialDrawable, 0, widthStart, widthEnd, isFirst, isLast);\n                    } else {\n                        drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine, isFirst, isLast);\n                        mCurrentDrawUsedWidth += size;\n                    }\n                } else if (mCurrentDrawLine == ellipsizeLine) {\n                    int needStop = getWidth() / 2 - mEllipsizeTextLength / 2;\n                    if (mIsExecutedMiddleEllipsize) {\n                        handleQQFaceAfterMiddleEllipsize(canvas, res, specialDrawable, widthStart, widthEnd, ellipsizeLine, isFirst, isLast);\n                    } else if (size + mCurrentDrawUsedWidth <= needStop) {\n                        drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine, isFirst, isLast);\n                        mCurrentDrawUsedWidth += size;\n                    } else {\n                        drawText(canvas, mEllipsizeText, 0, mEllipsizeText.length(), mEllipsizeTextLength);\n                        mMiddleEllipsizeWidthRecord = mCurrentDrawUsedWidth + mEllipsizeTextLength;\n                        mIsExecutedMiddleEllipsize = true;\n                        handleQQFaceAfterMiddleEllipsize(canvas, res, specialDrawable, widthStart, widthEnd, ellipsizeLine, isFirst, isLast);\n                    }\n                } else {\n                    handleQQFaceAfterMiddleEllipsize(canvas, res, specialDrawable, widthStart, widthEnd, ellipsizeLine, isFirst, isLast);\n                }\n            } else {\n                if (mCurrentDrawLine == mNeedDrawLine) {\n                    int ellipsizeLength = mMoreActionTextLength;\n                    if (mEllipsize == TextUtils.TruncateAt.END) {\n                        ellipsizeLength += mEllipsizeTextLength;\n                    }\n                    if (size + mCurrentDrawUsedWidth >= widthEnd - ellipsizeLength) {\n                        if (size + mCurrentDrawUsedWidth == widthEnd - ellipsizeLength) {\n                            drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine, isFirst, isLast);\n                            mCurrentDrawUsedWidth += size;\n                        }\n                        if (mEllipsize == TextUtils.TruncateAt.END) {\n                            drawText(canvas, mEllipsizeText, 0, mEllipsizeText.length(), mEllipsizeTextLength);\n                            mCurrentDrawUsedWidth += mEllipsizeTextLength;\n                        }\n                        drawMoreActionText(canvas, widthEnd);\n                        // 去新的一行，避免再次走入这一行的逻辑\n                        toNewDrawLine(widthStart, widthEnd - widthStart);\n                    } else {\n                        drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine, isFirst, isLast);\n                        mCurrentDrawUsedWidth += size;\n                    }\n                } else if (mCurrentDrawLine < mNeedDrawLine) {\n                    if (size + mCurrentDrawUsedWidth > widthEnd) {\n                        onRealDrawQQFace(canvas, res, specialDrawable, 0, widthStart, widthEnd, isFirst, isLast);\n                    } else {\n                        drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine, isFirst, isLast);\n                        mCurrentDrawUsedWidth += size;\n                    }\n                }\n            }\n\n        } else {\n            onRealDrawQQFace(canvas, res, specialDrawable, 0, widthStart, widthEnd, isFirst, isLast);\n        }\n    }\n\n    private int mLastNeedStopLineRecord = -1;\n\n    private void handleQQFaceAfterMiddleEllipsize(Canvas canvas, int res, Drawable specialDrawable, int widthStart,\n                                                  int widthEnd, int ellipsizeLine, boolean isFirst, boolean isLast) {\n        int size = res != 0 ? mQQFaceSize : specialDrawable.getIntrinsicWidth() + (isFirst || isLast ? mSpecialDrawablePadding : mSpecialDrawablePadding * 2);\n        if (mMiddleEllipsizeWidthRecord == -1) {\n            onRealDrawQQFace(canvas, res, specialDrawable, ellipsizeLine - mLastNeedStopLineRecord, widthStart, widthEnd, isFirst, isLast);\n            return;\n        }\n\n        int endLines = mNeedDrawLine - ellipsizeLine;\n        int borrowWidth = widthEnd - mCurrentCalWidth - (mMiddleEllipsizeWidthRecord - widthStart);\n        int needStopLine = borrowWidth > 0 ? mLines - endLines - 1 : mLines - endLines;\n        int needStopWidth = borrowWidth > 0 ? widthEnd - borrowWidth :\n                mMiddleEllipsizeWidthRecord - (widthEnd - mCurrentCalWidth);\n\n        if (mCurrentDrawLine < needStopLine) {\n            if (size + mCurrentDrawUsedWidth > widthEnd) {\n                toNewDrawLine(widthStart, widthEnd - widthStart);\n                onDrawQQFace(canvas, res, specialDrawable, widthStart, widthEnd, isFirst, isLast);\n            } else {\n                mCurrentDrawUsedWidth += size;\n            }\n        } else if (mCurrentDrawLine == needStopLine) {\n            if (size + mCurrentDrawUsedWidth <= needStopWidth) {\n                mCurrentDrawUsedWidth += size;\n            } else {\n                boolean drawCurrentFace = false;\n                if (mCurrentDrawUsedWidth >= needStopWidth) {\n                    drawCurrentFace = true;\n                }\n                mCurrentDrawUsedWidth = mMiddleEllipsizeWidthRecord;\n                mMiddleEllipsizeWidthRecord = -1;\n                mLastNeedStopLineRecord = needStopLine;\n                if (drawCurrentFace) {\n                    onDrawQQFace(canvas, res, specialDrawable, widthStart, widthEnd, isFirst, isLast);\n                }\n            }\n        } else {\n            onRealDrawQQFace(canvas, res, specialDrawable, ellipsizeLine - needStopLine, widthStart, widthEnd, isFirst, isLast);\n        }\n    }\n\n    private void onRealDrawQQFace(Canvas canvas, int res, @Nullable Drawable specialDrawable, int adjustLine,\n                                  int widthStart, int widthEnd, boolean isFirst, boolean isLast) {\n        int size = res != 0 || specialDrawable == null ? mQQFaceSize : specialDrawable.getIntrinsicWidth() + (isFirst || isLast ? mSpecialDrawablePadding : mSpecialDrawablePadding * 2);\n        if (mCurrentDrawUsedWidth + size > widthEnd) {\n            toNewDrawLine(widthStart, widthEnd - widthStart);\n        }\n        drawQQFace(canvas, res, specialDrawable, mCurrentDrawLine + adjustLine, isFirst, isLast);\n        mCurrentDrawUsedWidth += size;\n    }\n\n    private void drawQQFace(Canvas canvas, int res, @Nullable Drawable specialDrawable, int line, boolean isFirst, boolean isLast) {\n        Drawable drawable = res != 0 ? ContextCompat.getDrawable(getContext(), res) : specialDrawable;\n        int size = res != 0 || specialDrawable == null ? mQQFaceSize : specialDrawable.getIntrinsicWidth() + (isFirst || isLast ? mSpecialDrawablePadding : mSpecialDrawablePadding * 2);\n        if (drawable == null) {\n            return;\n        }\n        int drawableTop;\n        if (res != 0) {\n            drawableTop = (mFontHeight - mQQFaceSize) / 2;\n            drawable.setBounds(0, drawableTop, mQQFaceSize, drawableTop + mQQFaceSize);\n        } else {\n            int left = isLast ? mSpecialDrawablePadding : 0;\n            int drawableWidth = drawable.getIntrinsicWidth();\n            int drawableHeight = drawable.getIntrinsicHeight();\n            if (drawableHeight > mFontHeight) {\n                float scale = ((float) mFontHeight) / drawableHeight;\n                drawableHeight = mFontHeight;\n                drawableWidth = (int) (drawableWidth * scale);\n            }\n            drawableTop = (mFontHeight - drawableHeight) / 2;\n            drawable.setBounds(left, drawableTop, left + drawableWidth, drawableTop + drawableHeight);\n        }\n        int top = getPaddingTop();\n        if (line > 1) {\n            top = mCurrentDrawBaseLine - mFirstBaseLine;\n        }\n        canvas.save();\n        canvas.translate(mCurrentDrawUsedWidth, top);\n        if (mIsInDrawSpan && mCurrentDrawSpan != null) {\n            @ColorInt int color = mCurrentDrawSpan.isPressed() ? mCurrentDrawSpan.getPressedBackgroundColor() :\n                    mCurrentDrawSpan.getNormalBackgroundColor();\n            if (color != Color.TRANSPARENT) {\n                mDecorationPaint.setColor(color);\n                mDecorationPaint.setStyle(Paint.Style.FILL);\n                canvas.drawRect(0, 0, size, mFontHeight, mDecorationPaint);\n            }\n        }\n        drawable.draw(canvas);\n        if (mIsInDrawSpan && mCurrentDrawSpan != null &&\n                mCurrentDrawSpan.isNeedUnderline() && mLinkUnderLineHeight > 0) {\n            ColorStateList underLineColors = mLinkUnderLineColor == null ? mTextColor : mLinkUnderLineColor;\n            if (underLineColors != null) {\n                int underLineColor = underLineColors.getDefaultColor();\n                if (mCurrentDrawSpan.isPressed()) {\n                    underLineColor = underLineColors.getColorForState(mPressedState, underLineColor);\n                }\n                mDecorationPaint.setColor(underLineColor);\n                mDecorationPaint.setStyle(Paint.Style.STROKE);\n                mDecorationPaint.setStrokeWidth(mLinkUnderLineHeight);\n                canvas.drawLine(0, mFontHeight, size, mFontHeight, mDecorationPaint);\n            }\n        }\n        canvas.restore();\n    }\n\n    private class SpanInfo {\n        public static final int NOT_SET = -1;\n        private ITouchableSpan mTouchableSpan;\n        private int mStartPoint = NOT_SET;\n        private int mEndPoint = NOT_SET;\n        private int mStartLine = NOT_SET;\n        private int mEndLine = NOT_SET;\n\n        public SpanInfo(ITouchableSpan touchableSpan) {\n            mTouchableSpan = touchableSpan;\n        }\n\n        public void setStart(int startLine, int startPoint) {\n            mStartLine = startLine;\n            mStartPoint = startPoint;\n        }\n\n        public void setPressed(boolean pressed) {\n            mTouchableSpan.setPressed(pressed);\n        }\n\n        public void setEnd(int endLine, int endPoint) {\n            mEndLine = endLine;\n            mEndPoint = endPoint;\n        }\n\n        public void onClick() {\n            mTouchableSpan.onClick(QMUIQQFaceView.this);\n        }\n\n        public void invalidateSpan() {\n            int top = getPaddingTop();\n            if (mStartLine > 1) {\n                top = (mStartLine - 1) * (mFontHeight + mLineSpace) + top;\n            }\n\n            int bottom = (mEndLine - 1) * (mFontHeight + mLineSpace) + top + mFontHeight;\n            Rect bounds = new Rect();\n            bounds.top = top;\n            bounds.bottom = bottom;\n            bounds.left = getPaddingLeft();\n            bounds.right = getWidth() - getPaddingRight();\n            if (mStartLine == mEndLine) {\n                bounds.left = mStartPoint;\n                bounds.right = mEndPoint;\n            }\n            invalidate(bounds);\n        }\n\n        @SuppressWarnings(\"SimplifiableIfStatement\")\n        public boolean onTouch(int x, int y) {\n            int top = getPaddingTop();\n            if (mStartLine > 1) {\n                top = (mStartLine - 1) * (mFontHeight + mLineSpace) + top;\n            }\n\n            int bottom = (mEndLine - 1) * (mFontHeight + mLineSpace) + getPaddingTop() + mFontHeight;\n\n            if (y < top || y > bottom) {\n                return false;\n            }\n\n            if (mStartLine == mEndLine) {\n                return x >= mStartPoint && x <= mEndPoint;\n            }\n\n            int startLineBottom = top + mFontHeight;\n            int endLineTop = bottom - mFontHeight;\n            if (y > startLineBottom && y < endLineTop) {\n                //noinspection SimplifiableIfStatement\n                if (mEndLine - mStartLine == 1) {\n                    return x >= mStartPoint && x <= mEndPoint;\n                }\n                return true;\n            } else if (y <= startLineBottom) {\n                return x >= mStartPoint;\n            } else {\n                return x <= mEndPoint;\n            }\n\n        }\n    }\n\n    public static class PressCancelAction implements Runnable {\n        private WeakReference<SpanInfo> mWeakReference;\n\n        public PressCancelAction(SpanInfo spanInfo) {\n            mWeakReference = new WeakReference<>(spanInfo);\n        }\n\n        @Override\n        public void run() {\n            SpanInfo spanInfo = mWeakReference.get();\n            if (spanInfo != null) {\n                spanInfo.setPressed(false);\n                spanInfo.invalidateSpan();\n            }\n        }\n    }\n\n    public interface QQFaceViewListener {\n        void onCalculateLinesChange(int lines);\n\n        void onMoreTextClick();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/qqface/QQFace.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.qqface;\n\n/**\n * @author cginechen\n * @date 2016-12-21\n */\n\npublic class QQFace {\n    private String name;\n    private int res;\n\n    public QQFace(String name, int res) {\n        this.name = name;\n        this.res = res;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public int getRes() {\n        return res;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/recyclerView/QMUIRVDraggableScrollBar.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.recyclerView;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.graphics.drawable.DrawableCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerDecoration;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionLayout;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUIRVDraggableScrollBar extends RecyclerView.ItemDecoration implements IQMUISkinHandlerDecoration, QMUIStickySectionLayout.DrawDecoration {\n    private int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};\n    private int[] STATE_NORMAL = new int[]{};\n    private static final long DEFAULT_KEE_SHOW_DURATION = 800L;\n    private static final long DEFAULT_TRANSITION_DURATION = 100L;\n    private static final int MIN_COUNT_FOR_PERCENT_CALCULATE = 1000;\n\n    RecyclerView mRecyclerView;\n    QMUIStickySectionLayout mStickySectionLayout;\n    private final int mStartMargin;\n    private final int mEndMargin;\n    private final int mInwardOffset;\n    private final boolean mIsVerticalScroll;\n    private final boolean mIsLocationInOppositeSide;\n    private boolean mIsInDragging;\n    private Drawable mScrollBarDrawable;\n    private boolean mEnableScrollBarFadeInOut = false;\n    private boolean mIsDraggable = true;\n    private Callback mCallback;\n\n    private long mKeepShownTime = DEFAULT_KEE_SHOW_DURATION;\n    private long mTransitionDuration = DEFAULT_TRANSITION_DURATION;\n    private long mStartTransitionTime = 0;\n    private int mBeginAlpha = -1;\n    private int mTargetAlpha = -1;\n    private int mCurrentAlpha = 255;\n    private float mPercent = 0f;\n    private int mDragInnerStart = 0;\n    private int mScrollBarSkinRes = 0;\n    private int mScrollBarSkinTintColorRes = 0;\n\n    public QMUIRVDraggableScrollBar(int startMargin,\n                                    int endMargin,\n                                    int inwardOffset,\n                                    boolean isVerticalScroll,\n                                    boolean isLocationInOppositeSide) {\n        mStartMargin = startMargin;\n        mEndMargin = endMargin;\n        mInwardOffset = inwardOffset;\n        mIsVerticalScroll = isVerticalScroll;\n        mIsLocationInOppositeSide = isLocationInOppositeSide;\n    }\n\n    public QMUIRVDraggableScrollBar(int startMargin,\n                                    int endMargin,\n                                    int inwardOffset) {\n        this(startMargin, endMargin, inwardOffset, true, false);\n    }\n\n    private Runnable mFadeScrollBarAction = new Runnable() {\n        @Override\n        public void run() {\n            mTargetAlpha = 0;\n            mBeginAlpha = mCurrentAlpha;\n            mStartTransitionTime = System.currentTimeMillis();\n            invalidate();\n        }\n    };\n\n    private final RecyclerView.OnItemTouchListener mOnItemTouchListener = new RecyclerView.OnItemTouchListener() {\n\n        @Override\n        public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {\n            if (!mIsDraggable || mScrollBarDrawable == null || !needDrawScrollBar(rv)) {\n                return false;\n            }\n            int action = e.getAction();\n            final int x = (int) e.getX();\n            final int y = (int) e.getY();\n            if (action == MotionEvent.ACTION_DOWN) {\n                Rect bounds = mScrollBarDrawable.getBounds();\n                if (mCurrentAlpha > 0 && bounds.contains(x, y)) {\n                    startDrag();\n                    mDragInnerStart = mIsVerticalScroll ? y - bounds.top : x - bounds.left;\n                }\n            } else if (action == MotionEvent.ACTION_MOVE) {\n                if (mIsInDragging) {\n                    onDragging(rv, mScrollBarDrawable, x, y);\n                }\n            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n                if (mIsInDragging) {\n                    if(action == MotionEvent.ACTION_UP){\n                        onDragging(rv, mScrollBarDrawable, x, y);\n                    }\n                    endDrag();\n                }\n            }\n            return mIsInDragging;\n        }\n\n        @Override\n        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {\n            if (!mIsDraggable || mScrollBarDrawable == null || !needDrawScrollBar(rv)) {\n                return;\n            }\n            int action = e.getAction();\n            final int x = (int) e.getX();\n            final int y = (int) e.getY();\n            if (action == MotionEvent.ACTION_DOWN) {\n                Rect bounds = mScrollBarDrawable.getBounds();\n                if (mCurrentAlpha > 0 && bounds.contains(x, y)) {\n                    startDrag();\n                    mDragInnerStart = mIsVerticalScroll ? y - bounds.top : x - bounds.left;\n                }\n            } else if (action == MotionEvent.ACTION_MOVE) {\n                if (mIsInDragging) {\n                    onDragging(rv, mScrollBarDrawable, x, y);\n                }\n            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n                if (mIsInDragging) {\n                    if(action == MotionEvent.ACTION_UP) {\n                        onDragging(rv, mScrollBarDrawable, x, y);\n                    }\n                    endDrag();\n                }\n            }\n        }\n\n        @Override\n        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {\n            if (disallowIntercept && mIsInDragging) {\n                endDrag();\n            }\n        }\n    };\n\n    private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {\n\n        private int mPrevStatus = RecyclerView.SCROLL_STATE_IDLE;\n\n        @Override\n        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {\n            if (mEnableScrollBarFadeInOut) {\n                if (mPrevStatus == RecyclerView.SCROLL_STATE_IDLE && newState != RecyclerView.SCROLL_STATE_IDLE) {\n                    mStartTransitionTime = System.currentTimeMillis();\n                    mBeginAlpha = mCurrentAlpha;\n                    mTargetAlpha = 255;\n                    invalidate();\n                } else if (newState == RecyclerView.SCROLL_STATE_IDLE) {\n                    recyclerView.postDelayed(mFadeScrollBarAction, mKeepShownTime);\n                }\n            }\n            mPrevStatus = newState;\n        }\n    };\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    private void invalidate() {\n        if (mStickySectionLayout != null) {\n            mStickySectionLayout.invalidate();\n        } else if (mRecyclerView != null) {\n            mRecyclerView.invalidate();\n        }\n    }\n\n    public void setScrollBarDrawable(@Nullable Drawable scrollBarDrawable) {\n        mScrollBarDrawable = scrollBarDrawable;\n        if (scrollBarDrawable != null) {\n            scrollBarDrawable.setState(mIsInDragging ? STATE_PRESSED : STATE_NORMAL);\n        }\n        if (mRecyclerView != null) {\n            QMUISkinHelper.refreshRVItemDecoration(mRecyclerView, this);\n        }\n        invalidate();\n    }\n\n    public void setScrollBarSkinRes(int scrollBarSkinRes) {\n        mScrollBarSkinRes = scrollBarSkinRes;\n        if (mRecyclerView != null) {\n            QMUISkinHelper.refreshRVItemDecoration(mRecyclerView, this);\n        }\n        invalidate();\n    }\n\n    public void setScrollBarSkinTintColorRes(int colorRes) {\n        mScrollBarSkinTintColorRes = colorRes;\n        if (mRecyclerView != null) {\n            QMUISkinHelper.refreshRVItemDecoration(mRecyclerView, this);\n        }\n        invalidate();\n    }\n\n    public void setDraggable(boolean draggable) {\n        mIsDraggable = draggable;\n    }\n\n    public boolean isDraggable() {\n        return mIsDraggable;\n    }\n\n    public void setEnableScrollBarFadeInOut(boolean enableScrollBarFadeInOut) {\n        if (mEnableScrollBarFadeInOut != enableScrollBarFadeInOut) {\n            mEnableScrollBarFadeInOut = enableScrollBarFadeInOut;\n            if (!mEnableScrollBarFadeInOut) {\n                mBeginAlpha = -1;\n                mTargetAlpha = -1;\n                mCurrentAlpha = 255;\n            } else {\n                if (mRecyclerView != null) {\n                    if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {\n                        mCurrentAlpha = 0;\n                    }\n                } else {\n                    mCurrentAlpha = 0;\n                }\n            }\n            invalidate();\n        }\n    }\n\n    public boolean isEnableScrollBarFadeInOut() {\n        return mEnableScrollBarFadeInOut;\n    }\n\n    private void commonAttachToRecyclerView(@Nullable RecyclerView recyclerView) {\n        if (mRecyclerView == recyclerView) {\n            return; // nothing to do\n        }\n        if (mRecyclerView != null) {\n            destroyCallbacks();\n        }\n        mRecyclerView = recyclerView;\n        if (recyclerView != null) {\n            setupCallbacks();\n            QMUISkinHelper.refreshRVItemDecoration(recyclerView, this);\n        }\n    }\n\n    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {\n        if (mStickySectionLayout != null) {\n            mStickySectionLayout.removeDrawDecoration(this);\n            mStickySectionLayout = null;\n        }\n        commonAttachToRecyclerView(recyclerView);\n    }\n\n    public void attachToStickSectionLayout(@Nullable QMUIStickySectionLayout stickySectionLayout) {\n        if (mStickySectionLayout == stickySectionLayout) {\n            return; // nothing to do\n        }\n        if (mStickySectionLayout != null) {\n            mStickySectionLayout.removeDrawDecoration(this);\n        }\n        mStickySectionLayout = stickySectionLayout;\n        if (stickySectionLayout != null) {\n            stickySectionLayout.addDrawDecoration(this);\n            commonAttachToRecyclerView(stickySectionLayout.getRecyclerView());\n        }\n    }\n\n    private void setupCallbacks() {\n        mRecyclerView.addItemDecoration(this);\n        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);\n        mRecyclerView.addOnScrollListener(mScrollListener);\n    }\n\n    private void destroyCallbacks() {\n        mRecyclerView.removeItemDecoration(this);\n        mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);\n        mRecyclerView.removeCallbacks(mFadeScrollBarAction);\n        mRecyclerView.removeOnScrollListener(mScrollListener);\n    }\n\n    private void startDrag() {\n        mIsInDragging = true;\n        if (mScrollBarDrawable != null) {\n            mScrollBarDrawable.setState(STATE_PRESSED);\n        }\n        if (mCallback != null) {\n            mCallback.onDragStarted();\n        }\n        if (mRecyclerView != null) {\n            mRecyclerView.removeCallbacks(mFadeScrollBarAction);\n        }\n        invalidate();\n    }\n\n    private void endDrag() {\n        mIsInDragging = false;\n        if (mScrollBarDrawable != null) {\n            mScrollBarDrawable.setState(STATE_NORMAL);\n        }\n        if (mCallback != null) {\n            mCallback.onDragEnd();\n        }\n        invalidate();\n    }\n\n    private void onDragging(RecyclerView recyclerView, Drawable drawable, int x, int y) {\n        int drawableWidth = drawable.getIntrinsicWidth();\n        int drawableHeight = drawable.getIntrinsicHeight();\n        int usefulSpace = getUsefulSpace(recyclerView) - (mIsVerticalScroll ? drawableHeight : drawableWidth);\n        int useValue = mIsVerticalScroll ? y : x;\n        float percent = (useValue - mStartMargin - mDragInnerStart) * 1f / usefulSpace;\n        percent = QMUILangHelper.constrain(percent, 0f, 1f);\n        if (mCallback != null) {\n            mCallback.onDragToPercent(percent);\n        }\n        mPercent = percent;\n\n        if (percent <= 0) {\n            recyclerView.scrollToPosition(0);\n        } else if (percent >= 1f) {\n            RecyclerView.Adapter adapter = recyclerView.getAdapter();\n            if (adapter != null) {\n                recyclerView.scrollToPosition(adapter.getItemCount() - 1);\n            }\n        } else {\n            RecyclerView.Adapter adapter = recyclerView.getAdapter();\n            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();\n            if(adapter != null && adapter.getItemCount() > MIN_COUNT_FOR_PERCENT_CALCULATE && layoutManager instanceof LinearLayoutManager){\n                ((LinearLayoutManager)layoutManager).scrollToPositionWithOffset((int) (adapter.getItemCount() * mPercent), 0);\n            }else{\n                int range = getScrollRange(recyclerView);\n                int offset = getCurrentOffset(recyclerView);\n                int delta = (int) (range * mPercent - offset);\n                if (mIsVerticalScroll) {\n                    recyclerView.scrollBy(0, delta);\n                } else {\n                    recyclerView.scrollBy(delta, 0);\n                }\n            }\n        }\n        invalidate();\n    }\n\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        if (mStickySectionLayout == null) {\n            drawScrollBar(c, parent);\n        }\n    }\n\n    @Override\n    public void onDraw(@NonNull Canvas c, @NonNull QMUIStickySectionLayout parent) {\n\n    }\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull QMUIStickySectionLayout parent) {\n        if (mRecyclerView != null) {\n            drawScrollBar(c, mRecyclerView);\n        }\n    }\n\n    private void drawScrollBar(@NonNull Canvas c, @NonNull RecyclerView recyclerView) {\n        Drawable drawable = ensureScrollBar(recyclerView.getContext());\n        if (drawable == null || !needDrawScrollBar(recyclerView)) {\n            return;\n        }\n\n        if (mTargetAlpha != -1 && mBeginAlpha != -1) {\n            long transitionTime = System.currentTimeMillis() - mStartTransitionTime;\n            long duration = mTransitionDuration * Math.abs(mTargetAlpha - mBeginAlpha) / 255;\n            if (transitionTime >= duration) {\n                mCurrentAlpha = mTargetAlpha;\n                mTargetAlpha = -1;\n                mBeginAlpha = -1;\n            } else {\n                mCurrentAlpha = (int) (mBeginAlpha + (mTargetAlpha - mBeginAlpha) * transitionTime * 1f / duration);\n                recyclerView.postInvalidateOnAnimation();\n            }\n        }\n\n        drawable.setAlpha(mCurrentAlpha);\n\n        if (!mIsInDragging) {\n            mPercent = calculatePercent(recyclerView);\n        }\n        setScrollBarBounds(recyclerView, drawable);\n        drawable.draw(c);\n    }\n\n    private int getUsefulSpace(@NonNull RecyclerView recyclerView) {\n        if (mIsVerticalScroll) {\n            return recyclerView.getHeight() - mStartMargin - mEndMargin;\n        }\n        return recyclerView.getWidth() - mStartMargin - mEndMargin;\n    }\n\n    private boolean needDrawScrollBar(RecyclerView recyclerView){\n        if(mIsVerticalScroll){\n            return recyclerView.canScrollVertically(-1) || recyclerView.canScrollVertically(1);\n        }\n        return recyclerView.canScrollHorizontally(-1) || recyclerView.canScrollHorizontally(1);\n    }\n\n    private void setScrollBarBounds(@NonNull RecyclerView recyclerView, @NonNull Drawable drawable) {\n        int usefulSpace = getUsefulSpace(recyclerView);\n        int drawableWidth = drawable.getIntrinsicWidth();\n        int drawableHeight = drawable.getIntrinsicHeight();\n        int left, top;\n        if (mIsVerticalScroll) {\n            top = (int) ((usefulSpace - drawableHeight) * mPercent);\n            left = mIsLocationInOppositeSide ? mInwardOffset : (recyclerView.getWidth() - drawableWidth - mInwardOffset);\n\n        } else {\n            left = (int) ((usefulSpace - drawableWidth) * mPercent);\n            top = mIsLocationInOppositeSide ? mInwardOffset : (recyclerView.getHeight() - drawableHeight - mInwardOffset);\n        }\n        drawable.setBounds(left, top, left + drawableWidth, top + drawableHeight);\n    }\n\n    private int getScrollRange(@NonNull RecyclerView recyclerView) {\n        if (mIsVerticalScroll) {\n            return recyclerView.computeVerticalScrollRange() - recyclerView.getHeight();\n        } else {\n            return recyclerView.computeHorizontalScrollRange() - recyclerView.getWidth();\n        }\n    }\n\n    private int getCurrentOffset(@NonNull RecyclerView recyclerView) {\n        if (mIsVerticalScroll) {\n            return recyclerView.computeVerticalScrollOffset();\n        }\n        return recyclerView.computeHorizontalScrollOffset();\n    }\n\n    private float calculatePercent(@NonNull RecyclerView recyclerView) {\n        RecyclerView.Adapter adapter = recyclerView.getAdapter();\n        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();\n        if(adapter != null && adapter.getItemCount() > MIN_COUNT_FOR_PERCENT_CALCULATE && layoutManager instanceof LinearLayoutManager){\n            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;\n            return linearLayoutManager.findFirstCompletelyVisibleItemPosition() * 1f / adapter.getItemCount();\n        }\n        return QMUILangHelper.constrain(getCurrentOffset(recyclerView) * 1f / getScrollRange(recyclerView), 0f, 1f);\n    }\n\n    public Drawable ensureScrollBar(Context context) {\n        if (mScrollBarDrawable == null) {\n            setScrollBarDrawable(\n                    ContextCompat.getDrawable(context, R.drawable.qmui_icon_scroll_bar));\n        }\n        return mScrollBarDrawable;\n    }\n\n    @Override\n    public void handle(@NotNull @NonNull RecyclerView recyclerView,\n                       @NotNull @NonNull QMUISkinManager manager,\n                       int skinIndex,\n                       @NotNull @NonNull Resources.Theme theme) {\n        if (mScrollBarSkinRes != 0) {\n            mScrollBarDrawable = QMUIResHelper.getAttrDrawable(\n                    recyclerView.getContext(), theme, mScrollBarSkinRes);\n        } else if (mScrollBarSkinTintColorRes != 0 && mScrollBarDrawable != null) {\n            DrawableCompat.setTintList(mScrollBarDrawable,\n                    QMUIResHelper.getAttrColorStateList(\n                            recyclerView.getContext(), theme, mScrollBarSkinTintColorRes));\n        }\n        invalidate();\n    }\n\n    public interface Callback {\n        void onDragStarted();\n\n        void onDragToPercent(float percent);\n\n        void onDragEnd();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/recyclerView/QMUIRVItemSwipeAction.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.recyclerView;\n\nimport android.animation.Animator;\nimport android.animation.TimeInterpolator;\nimport android.animation.ValueAnimator;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewParent;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.R;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUIRVItemSwipeAction extends RecyclerView.ItemDecoration\n        implements RecyclerView.OnChildAttachStateChangeListener {\n    public static final int SWIPE_NONE = 0;\n    public static final int SWIPE_LEFT = 1;\n    public static final int SWIPE_RIGHT = 2;\n    public static final int SWIPE_UP = 3;\n    public static final int SWIPE_DOWN = 4;\n\n    public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1;\n    public static final int ANIMATION_TYPE_SWIPE_CANCEL = 2;\n    public static final int ANIMATION_TYPE_SWIPE_ACTION = 3;\n\n    private static final int ACTIVE_POINTER_ID_NONE = -1;\n\n    private static final int PIXELS_PER_SECOND = 1000;\n    private static final int SWIPE_TRIGGERED_IMMEDIATELY = -1;\n\n    private static final String TAG = \"QMUIRVItemSwipeAction\";\n\n    private static final boolean DEBUG = false;\n\n    /**\n     * Views, whose state should be cleared after they are detached from RecyclerView.\n     * This is necessary after swipe dismissing an item. We wait until animator finishes its job\n     * to clean these views.\n     */\n    final List<View> mPendingCleanup = new ArrayList<>();\n\n    /**\n     * Re-use array to calculate dx dy for a ViewHolder\n     */\n    private final float[] mTmpPosition = new float[2];\n\n    /**\n     * The reference coordinates for the action start. For drag & drop, this is the time long\n     * press is completed vs for swipe, this is the initial touch point.\n     */\n    float mInitialTouchX;\n\n    float mInitialTouchY;\n\n    long mDownTimeMillis = 0;\n\n    /**\n     * Set when ItemTouchHelper is assigned to a RecyclerView.\n     */\n    private float mSwipeEscapeVelocity;\n\n    /**\n     * Set when ItemTouchHelper is assigned to a RecyclerView.\n     */\n    private float mMaxSwipeVelocity;\n\n    /**\n     * The diff between the last event and initial touch.\n     */\n    float mDx;\n\n    float mDy;\n\n    /**\n     * The pointer we are tracking.\n     */\n    int mActivePointerId = ACTIVE_POINTER_ID_NONE;\n\n    /**\n     * When a View is swiped and needs to go back to where it was, we create a Recover\n     * Animation and animate it to its location using this custom Animator, instead of using\n     * framework Animators.\n     * Using framework animators has the side effect of clashing with ItemAnimator, creating\n     * jumpy UIs.\n     */\n    List<RecoverAnimation> mRecoverAnimations = new ArrayList<>();\n\n    private int mSlop;\n\n    RecyclerView mRecyclerView;\n\n    /**\n     * Used for detecting fling swipe\n     */\n    VelocityTracker mVelocityTracker;\n\n    private long mPressTimeToSwipe = SWIPE_TRIGGERED_IMMEDIATELY;\n\n    /**\n     * The coordinates of the selected view at the time it is selected. We record these values\n     * when action starts so that we can consistently position it even if LayoutManager moves the\n     * View.\n     */\n    float mSelectedStartX;\n    float mSelectedStartY;\n\n    int mSwipeDirection;\n\n    private MotionEvent mCurrentDownEvent;\n    private Runnable mLongPressToSwipe = new Runnable() {\n        @Override\n        public void run() {\n            if (mCurrentDownEvent != null) {\n                final int activePointerIndex = mCurrentDownEvent.findPointerIndex(mActivePointerId);\n                if (activePointerIndex >= 0) {\n                    checkSelectForSwipe(mCurrentDownEvent.getAction(), mCurrentDownEvent, activePointerIndex, true);\n                }\n            }\n        }\n    };\n\n    /**\n     * Currently selected view holder\n     */\n    RecyclerView.ViewHolder mSelected = null;\n\n    private final RecyclerView.OnItemTouchListener mOnItemTouchListener = new RecyclerView.OnItemTouchListener() {\n        @Override\n        public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,\n                                             @NonNull MotionEvent event) {\n            if (DEBUG) {\n                Log.d(TAG, \"intercept: x:\" + event.getX() + \",y:\" + event.getY() + \", \" + event);\n            }\n            final int action = event.getActionMasked();\n            if (action == MotionEvent.ACTION_DOWN) {\n                if (mCurrentDownEvent != null) {\n                    mCurrentDownEvent.recycle();\n                }\n                mCurrentDownEvent = MotionEvent.obtain(event);\n                if (mPressTimeToSwipe > 0 && mSelected == null) {\n                    recyclerView.postDelayed(mLongPressToSwipe, mPressTimeToSwipe);\n                }\n                mActivePointerId = event.getPointerId(0);\n                mInitialTouchX = event.getX();\n                mInitialTouchY = event.getY();\n                obtainVelocityTracker();\n                mDownTimeMillis = System.currentTimeMillis();\n                if (mSelected == null) {\n                    final RecoverAnimation animation = findAnimation(event);\n                    if (animation != null) {\n                        mInitialTouchX -= animation.mX;\n                        mInitialTouchY -= animation.mY;\n                        endRecoverAnimation(animation.mViewHolder, true);\n                        if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {\n                            mCallback.clearView(mRecyclerView, animation.mViewHolder);\n                        }\n                        select(animation.mViewHolder);\n                        updateDxDy(event, mSwipeDirection, 0);\n                    }\n                } else {\n                    if (mSelected instanceof QMUISwipeViewHolder) {\n                        QMUISwipeViewHolder swipeViewHolder = (QMUISwipeViewHolder) mSelected;\n                        boolean isDownToAction = swipeViewHolder.checkDown(mInitialTouchX, mInitialTouchY);\n                        if (!isDownToAction) {\n                            if (hitTest(mSelected.itemView,\n                                    mInitialTouchX, mInitialTouchY,\n                                    mSelectedStartX + mDx, mSelectedStartY + mDy)) {\n                                mInitialTouchX -= mDx;\n                                mInitialTouchY -= mDy;\n                            } else {\n                                select(null);\n                                return true;\n                            }\n                        } else {\n                            mInitialTouchX -= mDx;\n                            mInitialTouchY -= mDy;\n                        }\n                    }\n                }\n            } else if (action == MotionEvent.ACTION_CANCEL) {\n                mActivePointerId = ACTIVE_POINTER_ID_NONE;\n                mRecyclerView.removeCallbacks(mLongPressToSwipe);\n                select(null);\n            } else if (action == MotionEvent.ACTION_UP) {\n                mRecyclerView.removeCallbacks(mLongPressToSwipe);\n                handleActionUp(event.getX(), event.getY(), mSlop);\n                mActivePointerId = ACTIVE_POINTER_ID_NONE;\n            } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {\n                // in a non scroll orientation, if distance change is above threshold, we\n                // can select the item\n                final int index = event.findPointerIndex(mActivePointerId);\n                if (DEBUG) {\n                    Log.d(TAG, \"pointer index \" + index);\n                }\n                if (index >= 0) {\n                    checkSelectForSwipe(action, event, index, false);\n                }\n            }\n            if (mVelocityTracker != null) {\n                mVelocityTracker.addMovement(event);\n            }\n            return mSelected != null;\n        }\n\n        @Override\n        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent event) {\n            if (DEBUG) {\n                Log.d(TAG,\n                        \"on touch: x:\" + mInitialTouchX + \",y:\" + mInitialTouchY + \", :\" + event);\n            }\n            if (mVelocityTracker != null) {\n                mVelocityTracker.addMovement(event);\n            }\n            if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {\n                return;\n            }\n            final int action = event.getActionMasked();\n            final int activePointerIndex = event.findPointerIndex(mActivePointerId);\n            if (activePointerIndex >= 0) {\n                checkSelectForSwipe(action, event, activePointerIndex, false);\n            }\n            RecyclerView.ViewHolder viewHolder = mSelected;\n            if (viewHolder == null) {\n                return;\n            }\n            switch (action) {\n                case MotionEvent.ACTION_MOVE: {\n                    // Find the index of the active pointer and fetch its position\n                    if (activePointerIndex >= 0) {\n                        updateDxDy(event, mSwipeDirection, activePointerIndex);\n                        mRecyclerView.invalidate();\n\n                        final float x = event.getX(activePointerIndex);\n                        final float y = event.getY(activePointerIndex);\n                        if (Math.abs(x - mInitialTouchX) > mSlop ||\n                                Math.abs(y - mInitialTouchY) > mSlop) {\n                            mRecyclerView.removeCallbacks(mLongPressToSwipe);\n                        }\n                    }\n                    break;\n                }\n                case MotionEvent.ACTION_CANCEL:\n                    mRecyclerView.removeCallbacks(mLongPressToSwipe);\n                    select(null);\n                    if (mVelocityTracker != null) {\n                        mVelocityTracker.clear();\n                    }\n                    mActivePointerId = ACTIVE_POINTER_ID_NONE;\n                    break;\n                case MotionEvent.ACTION_UP:\n                    mRecyclerView.removeCallbacks(mLongPressToSwipe);\n                    handleActionUp(event.getX(), event.getY(), mSlop);\n                    if (mVelocityTracker != null) {\n                        mVelocityTracker.clear();\n                    }\n                    mActivePointerId = ACTIVE_POINTER_ID_NONE;\n                    break;\n                case MotionEvent.ACTION_POINTER_UP: {\n                    final int pointerIndex = event.getActionIndex();\n                    final int pointerId = event.getPointerId(pointerIndex);\n                    if (pointerId == mActivePointerId) {\n                        // This was our active pointer going up. Choose a new\n                        // active pointer and adjust accordingly.\n                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                        mActivePointerId = event.getPointerId(newPointerIndex);\n                        updateDxDy(event, mSwipeDirection, pointerIndex);\n                    }\n                    break;\n                }\n            }\n        }\n\n        @Override\n        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {\n            if (!disallowIntercept) {\n                return;\n            }\n            select(null);\n        }\n    };\n\n    private Callback mCallback;\n    private boolean mSwipeDeleteWhenOnlyOneAction = false;\n\n    public QMUIRVItemSwipeAction(boolean swipeDeleteWhenOnlyOneAction, Callback callback) {\n        mCallback = callback;\n        mSwipeDeleteWhenOnlyOneAction = swipeDeleteWhenOnlyOneAction;\n    }\n\n    /**\n     * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already\n     * attached to a RecyclerView, it will first detach from the previous one. You can call this\n     * method with {@code null} to detach it from the current RecyclerView.\n     *\n     * @param recyclerView The RecyclerView instance to which you want to add this helper or\n     *                     {@code null} if you want to remove ItemTouchHelper from the current\n     *                     RecyclerView.\n     */\n    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {\n        if (mRecyclerView == recyclerView) {\n            return; // nothing to do\n        }\n        if (mRecyclerView != null) {\n            destroyCallbacks();\n        }\n        mRecyclerView = recyclerView;\n        if (recyclerView != null) {\n            final Resources resources = recyclerView.getResources();\n            mSwipeEscapeVelocity = resources.getDimension(R.dimen.qmui_rv_swipe_action_escape_velocity);\n            mMaxSwipeVelocity = resources.getDimension(R.dimen.qmui_rv_swipe_action_escape_max_velocity);\n            setupCallbacks();\n        }\n    }\n\n    public void setPressTimeToSwipe(long pressTimeToSwipe) {\n        mPressTimeToSwipe = pressTimeToSwipe;\n    }\n\n    private void setupCallbacks() {\n        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());\n        mSlop = vc.getScaledTouchSlop();\n        mRecyclerView.addItemDecoration(this);\n        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);\n        mRecyclerView.addOnChildAttachStateChangeListener(this);\n    }\n\n    private void destroyCallbacks() {\n        mRecyclerView.removeItemDecoration(this);\n        mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);\n        mRecyclerView.removeOnChildAttachStateChangeListener(this);\n        // clean all attached\n        final int recoverAnimSize = mRecoverAnimations.size();\n        for (int i = recoverAnimSize - 1; i >= 0; i--) {\n            final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);\n            mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);\n        }\n        mRecoverAnimations.clear();\n        releaseVelocityTracker();\n    }\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        float dx = 0, dy = 0;\n        if (mSelected != null) {\n            getSelectedDxDy(mTmpPosition);\n            dx = mTmpPosition[0];\n            dy = mTmpPosition[1];\n        }\n        mCallback.onDrawOver(c, parent, mSelected, mRecoverAnimations, dx, dy);\n    }\n\n    @Override\n    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        float dx = 0, dy = 0;\n        if (mSelected != null) {\n            getSelectedDxDy(mTmpPosition);\n            dx = mTmpPosition[0];\n            dy = mTmpPosition[1];\n        }\n        mCallback.onDraw(c, parent, mSelected, mRecoverAnimations, dx, dy, mSwipeDirection);\n    }\n\n    @Override\n    public void onChildViewAttachedToWindow(@NonNull View view) {\n\n    }\n\n    @Override\n    public void onChildViewDetachedFromWindow(@NonNull View view) {\n        final RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);\n        if (holder == null) {\n            return;\n        }\n        if (mSelected != null && holder == mSelected) {\n            select(null);\n        } else {\n            endRecoverAnimation(holder, false); // this may push it into pending cleanup list.\n            if (mPendingCleanup.remove(holder.itemView)) {\n                mCallback.clearView(mRecyclerView, holder);\n            }\n        }\n    }\n\n    void updateDxDy(MotionEvent ev, int swipeDirection, int pointerIndex) {\n        final float x = ev.getX(pointerIndex);\n        final float y = ev.getY(pointerIndex);\n\n        // Calculate the distance moved\n        if (swipeDirection == SWIPE_RIGHT) {\n            mDx = Math.max(0, x - mInitialTouchX);\n            mDy = 0;\n        } else if (swipeDirection == SWIPE_LEFT) {\n            mDx = Math.min(0, x - mInitialTouchX);\n            mDy = 0;\n        } else if (swipeDirection == SWIPE_DOWN) {\n            mDx = 0;\n            mDy = Math.max(0, y - mInitialTouchY);\n        } else if (swipeDirection == SWIPE_UP) {\n            mDx = 0;\n            mDy = Math.min(0, y - mInitialTouchY);\n        }\n    }\n\n\n    /**\n     * Checks whether we should select a View for swiping.\n     */\n    void checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex, boolean isLongPressToSwipe) {\n        if (mSelected != null || (mPressTimeToSwipe == SWIPE_TRIGGERED_IMMEDIATELY && action != MotionEvent.ACTION_MOVE)) {\n            return;\n        }\n\n        if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {\n            return;\n        }\n\n        final RecyclerView.ViewHolder vh = findSwipedView(motionEvent, isLongPressToSwipe);\n        if (vh == null) {\n            return;\n        }\n\n        int swipeDirection = mCallback.getSwipeDirection(mRecyclerView, vh);\n        if (swipeDirection == SWIPE_NONE) {\n            return;\n        }\n\n        if (mPressTimeToSwipe == SWIPE_TRIGGERED_IMMEDIATELY) {\n            // mDx and mDy are only set in allowed directions. We use custom x/y here instead of\n            // updateDxDy to avoid swiping if user moves more in the other direction\n            final float x = motionEvent.getX(pointerIndex);\n            final float y = motionEvent.getY(pointerIndex);\n\n            // Calculate the distance moved\n            final float dx = x - mInitialTouchX;\n            final float dy = y - mInitialTouchY;\n            // swipe target is chose w/o applying flags so it does not really check if swiping in that\n            // direction is allowed. This why here, we use mDx mDy to check slope value again.\n            final float absDx = Math.abs(dx);\n            final float absDy = Math.abs(dy);\n\n            if (swipeDirection == SWIPE_LEFT) {\n                if (absDx < mSlop || dx >= 0) {\n                    return;\n                }\n            } else if (swipeDirection == SWIPE_RIGHT) {\n                if (absDx < mSlop || dx <= 0) {\n                    return;\n                }\n            } else if (swipeDirection == SWIPE_UP) {\n                if (absDy < mSlop || dy >= 0) {\n                    return;\n                }\n            } else if (swipeDirection == SWIPE_DOWN) {\n                if (absDy < mSlop || dy <= 0) {\n                    return;\n                }\n            }\n        } else {\n            if (mPressTimeToSwipe >= System.currentTimeMillis() - mDownTimeMillis) {\n                return;\n            }\n        }\n\n        mRecyclerView.removeCallbacks(mLongPressToSwipe);\n        mDx = mDy = 0f;\n        mActivePointerId = motionEvent.getPointerId(0);\n        MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);\n        cancelEvent.setAction(MotionEvent.ACTION_CANCEL);\n        vh.itemView.dispatchTouchEvent(cancelEvent);\n        cancelEvent.recycle();\n        select(vh);\n    }\n\n    public void clear() {\n        select(null, false);\n    }\n\n    void handleActionUp(float x, float y, int touchSlop) {\n        if (mSelected != null) {\n            if (mSelected instanceof QMUISwipeViewHolder) {\n                QMUISwipeViewHolder swipeViewHolder = (QMUISwipeViewHolder) mSelected;\n                if (!swipeViewHolder.hasAction()) {\n                    select(null, true);\n                } else if(swipeViewHolder.mSwipeActions.size() == 1 && mSwipeDeleteWhenOnlyOneAction){\n                    if(mCallback.isOverThreshold(mRecyclerView, mSelected, mDx, mDy, mSwipeDirection)){\n                        select(null, true);\n                    }else{\n                        handleSwipeActionActionUp(swipeViewHolder, x, y, touchSlop);\n                    }\n                } else {\n                    handleSwipeActionActionUp(swipeViewHolder, x, y, touchSlop);\n                }\n            } else {\n                select(null, true);\n            }\n        }\n    }\n\n    void handleSwipeActionActionUp( QMUISwipeViewHolder swipeViewHolder, float x, float y, int touchSlop){\n        QMUISwipeAction action = swipeViewHolder.checkUp(x, y, touchSlop);\n        if (action != null) {\n            mCallback.onClickAction(this, mSelected, action);\n            swipeViewHolder.clearTouchInfo();\n            return;\n        }\n        swipeViewHolder.clearTouchInfo();\n        final int swipeDir = checkSwipe(mSelected, mSwipeDirection, true);\n        if (swipeDir == SWIPE_NONE) {\n            select(null, true);\n        } else {\n            getSelectedDxDy(mTmpPosition);\n            final float currentTranslateX = mTmpPosition[0];\n            final float currentTranslateY = mTmpPosition[1];\n            final float targetTranslateX, targetTranslateY;\n            switch (swipeDir) {\n                case SWIPE_LEFT:\n                    targetTranslateY = 0;\n                    targetTranslateX = -swipeViewHolder.mActionTotalWidth;\n                    break;\n                case SWIPE_RIGHT:\n                    targetTranslateY = 0;\n                    targetTranslateX = swipeViewHolder.mActionTotalWidth;\n                    break;\n                case SWIPE_UP:\n                    targetTranslateX = 0;\n                    targetTranslateY = -swipeViewHolder.mActionTotalHeight;\n                    break;\n                case SWIPE_DOWN:\n                    targetTranslateX = 0;\n                    targetTranslateY = swipeViewHolder.mActionTotalHeight;\n                    break;\n                default:\n                    targetTranslateX = 0;\n                    targetTranslateY = 0;\n            }\n\n            mDx += targetTranslateX - currentTranslateX;\n            mDy += targetTranslateY - currentTranslateY;\n            final RecoverAnimation rv = new RecoverAnimation(swipeViewHolder,\n                    currentTranslateX, currentTranslateY,\n                    targetTranslateX, targetTranslateY,\n                    mCallback.getInterpolator(ANIMATION_TYPE_SWIPE_ACTION));\n            final long duration = mCallback.getAnimationDuration(mRecyclerView,\n                    ANIMATION_TYPE_SWIPE_ACTION,\n                    targetTranslateX - currentTranslateX,\n                    targetTranslateY - currentTranslateY);\n            rv.setDuration(duration);\n            mRecoverAnimations.add(rv);\n            rv.start();\n            mRecyclerView.invalidate();\n        }\n    }\n\n    void select(@Nullable RecyclerView.ViewHolder selected) {\n        select(selected, false);\n    }\n\n    void select(@Nullable RecyclerView.ViewHolder selected, boolean isActionUp) {\n        if (selected == mSelected) {\n            return;\n        }\n        // prevent duplicate animations\n        endRecoverAnimation(selected, true);\n\n        boolean preventLayout = false;\n\n        if (mSelected != null) {\n            final RecyclerView.ViewHolder prevSelected = mSelected;\n            if (prevSelected.itemView.getParent() != null) {\n                endRecoverAnimation(prevSelected, true);\n                final int swipeDir = isActionUp ? checkSwipe(mSelected, mSwipeDirection, false) : SWIPE_NONE;\n                getSelectedDxDy(mTmpPosition);\n                final float currentTranslateX = mTmpPosition[0];\n                final float currentTranslateY = mTmpPosition[1];\n                final float targetTranslateX, targetTranslateY;\n                switch (swipeDir) {\n                    case SWIPE_LEFT:\n                    case SWIPE_RIGHT:\n                        targetTranslateY = 0;\n                        targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();\n                        break;\n                    case SWIPE_UP:\n                    case SWIPE_DOWN:\n                        targetTranslateX = 0;\n                        targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();\n                        break;\n                    default:\n                        targetTranslateX = 0;\n                        targetTranslateY = 0;\n                }\n\n                final int animType = swipeDir > 0 ? ANIMATION_TYPE_SWIPE_SUCCESS : ANIMATION_TYPE_SWIPE_CANCEL;\n                if (swipeDir > 0) {\n                    mCallback.onStartSwipeAnimation(mSelected, swipeDir);\n                }\n                final RecoverAnimation rv = new RecoverAnimation(prevSelected,\n                        currentTranslateX, currentTranslateY,\n                        targetTranslateX, targetTranslateY,\n                        mCallback.getInterpolator(ANIMATION_TYPE_SWIPE_ACTION)) {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        super.onAnimationEnd(animation);\n                        if (this.mOverridden) {\n                            return;\n                        }\n                        if (swipeDir == SWIPE_NONE) {\n                            // this is a drag or failed swipe. recover immediately\n                            mCallback.clearView(mRecyclerView, prevSelected);\n                            // full cleanup will happen on onDrawOver\n                        } else {\n                            // wait until remove animation is complete.\n                            mPendingCleanup.add(prevSelected.itemView);\n                            mIsPendingCleanup = true;\n                            if (swipeDir > 0) {\n                                // Animation might be ended by other animators during a layout.\n                                // We defer callback to avoid editing adapter during a layout.\n                                postDispatchSwipe(this, swipeDir);\n                            }\n                        }\n                    }\n                };\n                final long duration = mCallback.getAnimationDuration(mRecyclerView, animType,\n                        targetTranslateX - currentTranslateX,\n                        targetTranslateY - currentTranslateY);\n                rv.setDuration(duration);\n                mRecoverAnimations.add(rv);\n                rv.start();\n                preventLayout = true;\n            } else {\n                mCallback.clearView(mRecyclerView, prevSelected);\n            }\n            mSelected = null;\n        }\n        if (selected != null) {\n            mSwipeDirection = mCallback.getSwipeDirection(mRecyclerView, selected);\n            mSelectedStartX = selected.itemView.getLeft();\n            mSelectedStartY = selected.itemView.getTop();\n            mSelected = selected;\n            if (selected instanceof QMUISwipeViewHolder) {\n                QMUISwipeViewHolder qmuiSwipeViewHolder = (QMUISwipeViewHolder) selected;\n                qmuiSwipeViewHolder.setup(mSwipeDirection, mSwipeDeleteWhenOnlyOneAction);\n            }\n        }\n        final ViewParent rvParent = mRecyclerView.getParent();\n        if (rvParent != null) {\n            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);\n        }\n        if (!preventLayout) {\n            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();\n        }\n        mCallback.onSelectedChanged(mSelected);\n        mRecyclerView.invalidate();\n    }\n\n    private void getSelectedDxDy(float[] outPosition) {\n        if (mSwipeDirection == SWIPE_LEFT || mSwipeDirection == SWIPE_RIGHT) {\n            outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();\n        } else {\n            outPosition[0] = mSelected.itemView.getTranslationX();\n        }\n        if (mSwipeDirection == SWIPE_UP || mSwipeDirection == SWIPE_DOWN) {\n            outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();\n        } else {\n            outPosition[1] = mSelected.itemView.getTranslationY();\n        }\n    }\n\n    void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) {\n        // wait until animations are complete.\n        mRecyclerView.post(new Runnable() {\n            @Override\n            public void run() {\n                if (mRecyclerView != null && mRecyclerView.isAttachedToWindow()\n                        && !anim.mOverridden\n                        && anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {\n                    final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();\n                    // if animator is running or we have other active recover animations, we try\n                    // not to call onSwiped because DefaultItemAnimator is not good at merging\n                    // animations. Instead, we wait and batch.\n                    if ((animator == null || !animator.isRunning(null))\n                            && !hasRunningRecoverAnim()) {\n                        mCallback.onSwiped(anim.mViewHolder, swipeDir);\n                    } else {\n                        mRecyclerView.post(this);\n                    }\n                }\n            }\n        });\n    }\n\n    boolean hasRunningRecoverAnim() {\n        final int size = mRecoverAnimations.size();\n        for (int i = 0; i < size; i++) {\n            if (!mRecoverAnimations.get(i).mEnded) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private int checkSwipe(RecyclerView.ViewHolder viewHolder, int swipeDirection, boolean checkAction) {\n        if (swipeDirection == SWIPE_LEFT || swipeDirection == SWIPE_RIGHT) {\n            final int dirFlag = mDx > 0 ? SWIPE_RIGHT : SWIPE_LEFT;\n            if (mVelocityTracker != null && mActivePointerId > -1) {\n                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,\n                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));\n                final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);\n                final int velDirFlag = xVelocity > 0f ? SWIPE_RIGHT : SWIPE_LEFT;\n                final float absXVelocity = Math.abs(xVelocity);\n                if (dirFlag == velDirFlag &&\n                        absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)) {\n                    return velDirFlag;\n                }\n            }\n\n            float threshold;\n            if (checkAction && viewHolder instanceof QMUISwipeViewHolder) {\n                threshold = ((QMUISwipeViewHolder) viewHolder).mActionTotalWidth;\n            } else {\n                threshold = mRecyclerView.getWidth() * mCallback.getSwipeThreshold(viewHolder);\n            }\n\n            if (Math.abs(mDx) >= threshold) {\n                return dirFlag;\n            }\n        } else if (swipeDirection == SWIPE_UP || swipeDirection == SWIPE_DOWN) {\n            final int dirFlag = mDy > 0 ? SWIPE_DOWN : SWIPE_UP;\n            if (mVelocityTracker != null && mActivePointerId > -1) {\n                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,\n                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));\n                final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);\n                final int velDirFlag = yVelocity > 0f ? SWIPE_DOWN : SWIPE_UP;\n                final float absYVelocity = Math.abs(yVelocity);\n                if (velDirFlag == dirFlag &&\n                        absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)) {\n                    return velDirFlag;\n                }\n            }\n\n            float threshold;\n            if (checkAction && viewHolder instanceof QMUISwipeViewHolder) {\n                threshold = ((QMUISwipeViewHolder) viewHolder).mActionTotalHeight;\n            } else {\n                threshold = mRecyclerView.getHeight() * mCallback.getSwipeThreshold(viewHolder);\n            }\n            if (Math.abs(mDy) >= threshold) {\n                return dirFlag;\n            }\n        }\n        return SWIPE_NONE;\n    }\n\n    @Nullable\n    private RecyclerView.ViewHolder findSwipedView(MotionEvent motionEvent, boolean isLongPressToSwipe) {\n        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();\n        if (mActivePointerId == ACTIVE_POINTER_ID_NONE || lm == null) {\n            return null;\n        }\n        if (isLongPressToSwipe) {\n            View child = findChildView(motionEvent);\n            if (child == null) {\n                return null;\n            }\n            return mRecyclerView.getChildViewHolder(child);\n        }\n        final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);\n        final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;\n        final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;\n        final float absDx = Math.abs(dx);\n        final float absDy = Math.abs(dy);\n\n        if (absDx < mSlop && absDy < mSlop) {\n            return null;\n        }\n        if (absDx > absDy && lm.canScrollHorizontally()) {\n            return null;\n        } else if (absDy > absDx && lm.canScrollVertically()) {\n            return null;\n        }\n        View child = findChildView(motionEvent);\n        if (child == null) {\n            return null;\n        }\n        return mRecyclerView.getChildViewHolder(child);\n    }\n\n    void endRecoverAnimation(RecyclerView.ViewHolder viewHolder, boolean override) {\n        final int recoverAnimSize = mRecoverAnimations.size();\n        for (int i = recoverAnimSize - 1; i >= 0; i--) {\n            final RecoverAnimation anim = mRecoverAnimations.get(i);\n            if (anim.mViewHolder == viewHolder) {\n                anim.mOverridden |= override;\n                if (!anim.mEnded) {\n                    anim.cancel();\n                }\n                mRecoverAnimations.remove(i);\n                return;\n            }\n        }\n    }\n\n    View findChildView(MotionEvent event) {\n        // first check elevated views, if none, then call RV\n        final float x = event.getX();\n        final float y = event.getY();\n        if (mSelected != null) {\n            final View selectedView = mSelected.itemView;\n            if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {\n                return selectedView;\n            }\n        }\n        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {\n            final RecoverAnimation anim = mRecoverAnimations.get(i);\n            final View view = anim.mViewHolder.itemView;\n            if (hitTest(view, x, y, view.getX(), view.getY())) {\n                return view;\n            }\n        }\n        return mRecyclerView.findChildViewUnder(x, y);\n    }\n\n    @Nullable\n    RecoverAnimation findAnimation(MotionEvent event) {\n        if (mRecoverAnimations.isEmpty()) {\n            return null;\n        }\n        View target = findChildView(event);\n        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {\n            final RecoverAnimation anim = mRecoverAnimations.get(i);\n            if (anim.mViewHolder.itemView == target) {\n                return anim;\n            }\n        }\n        return null;\n    }\n\n    void obtainVelocityTracker() {\n        if (mVelocityTracker != null) {\n            mVelocityTracker.recycle();\n        }\n        mVelocityTracker = VelocityTracker.obtain();\n    }\n\n    private void releaseVelocityTracker() {\n        if (mVelocityTracker != null) {\n            mVelocityTracker.recycle();\n            mVelocityTracker = null;\n        }\n    }\n\n    private static boolean hitTest(View child, float x, float y, float left, float top) {\n        return x >= left\n                && x <= left + child.getWidth()\n                && y >= top\n                && y <= top + child.getHeight();\n    }\n\n    private static class RecoverAnimation implements Animator.AnimatorListener {\n\n        final float mStartDx;\n\n        final float mStartDy;\n\n        final float mTargetX;\n\n        final float mTargetY;\n\n        final RecyclerView.ViewHolder mViewHolder;\n\n        private final ValueAnimator mValueAnimator;\n\n        boolean mIsPendingCleanup;\n\n        float mX;\n\n        float mY;\n\n        // if user starts touching a recovering view, we put it into interaction mode again,\n        // instantly.\n        boolean mOverridden = false;\n\n        boolean mEnded = false;\n\n        private float mFraction;\n\n        RecoverAnimation(RecyclerView.ViewHolder viewHolder,\n                         float startDx, float startDy, float targetX, float targetY,\n                         TimeInterpolator interpolator) {\n            mViewHolder = viewHolder;\n            mStartDx = startDx;\n            mStartDy = startDy;\n            mTargetX = targetX;\n            mTargetY = targetY;\n            mValueAnimator = ValueAnimator.ofFloat(0f, 1f);\n            mValueAnimator.addUpdateListener(\n                    new ValueAnimator.AnimatorUpdateListener() {\n                        @Override\n                        public void onAnimationUpdate(ValueAnimator animation) {\n                            setFraction(animation.getAnimatedFraction());\n                        }\n                    });\n            mValueAnimator.setTarget(viewHolder.itemView);\n            mValueAnimator.addListener(this);\n            mValueAnimator.setInterpolator(interpolator);\n            setFraction(0f);\n        }\n\n        public void setDuration(long duration) {\n            mValueAnimator.setDuration(duration);\n        }\n\n        public void start() {\n            mViewHolder.setIsRecyclable(false);\n            mValueAnimator.start();\n        }\n\n        public void cancel() {\n            mValueAnimator.cancel();\n        }\n\n        public void setFraction(float fraction) {\n            mFraction = fraction;\n        }\n\n        /**\n         * We run updates on onDraw method but use the fraction from animator callback.\n         * This way, we can sync translate x/y values w/ the animators to avoid one-off frames.\n         */\n        public void update() {\n            if (mStartDx == mTargetX) {\n                mX = mViewHolder.itemView.getTranslationX();\n            } else {\n                mX = mStartDx + mFraction * (mTargetX - mStartDx);\n            }\n            if (mStartDy == mTargetY) {\n                mY = mViewHolder.itemView.getTranslationY();\n            } else {\n                mY = mStartDy + mFraction * (mTargetY - mStartDy);\n            }\n        }\n\n        @Override\n        public void onAnimationStart(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationEnd(Animator animation) {\n            if (!mEnded) {\n                mViewHolder.setIsRecyclable(true);\n            }\n            mEnded = true;\n        }\n\n        @Override\n        public void onAnimationCancel(Animator animation) {\n            setFraction(1f); //make sure we recover the view's state.\n        }\n\n        @Override\n        public void onAnimationRepeat(Animator animation) {\n\n        }\n    }\n\n    public static abstract class Callback {\n        public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;\n\n        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {\n            View view = viewHolder.itemView;\n            view.setTranslationX(0);\n            view.setTranslationY(0);\n            if (viewHolder instanceof QMUISwipeViewHolder) {\n                ((QMUISwipeViewHolder) viewHolder).clearTouchInfo();\n            }\n        }\n\n        public int getSwipeDirection(@NonNull RecyclerView recyclerView,\n                                     @NonNull RecyclerView.ViewHolder viewHolder) {\n            return SWIPE_NONE;\n        }\n\n        public void onStartSwipeAnimation(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n\n        }\n\n        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n\n        }\n\n\n        public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {\n            return .5f;\n        }\n\n        public float getSwipeEscapeVelocity(float defaultValue) {\n            return defaultValue;\n        }\n\n\n        public float getSwipeVelocityThreshold(float defaultValue) {\n            return defaultValue;\n        }\n\n\n        public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType,\n                                         float animateDx, float animateDy) {\n            return DEFAULT_SWIPE_ANIMATION_DURATION;\n        }\n\n        public void onSelectedChanged(RecyclerView.ViewHolder selected) {\n\n        }\n\n        public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n\n        }\n\n        public TimeInterpolator getInterpolator(int animationType) {\n            return null;\n        }\n\n        void onDraw(Canvas c, RecyclerView parent, RecyclerView.ViewHolder selected,\n                    List<RecoverAnimation> recoverAnimationList, float dX, float dY, int swipeDirection) {\n            final int recoverAnimSize = recoverAnimationList.size();\n            for (int i = 0; i < recoverAnimSize; i++) {\n                final RecoverAnimation anim = recoverAnimationList.get(i);\n                anim.update();\n                if (anim.mViewHolder == selected) {\n                    dX = anim.mX;\n                    dY = anim.mY;\n                } else {\n                    final int count = c.save();\n                    onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, false, swipeDirection);\n                    c.restoreToCount(count);\n                }\n\n            }\n            if (selected != null) {\n                final int count = c.save();\n                onChildDraw(c, parent, selected, dX, dY, true, swipeDirection);\n                c.restoreToCount(count);\n            }\n        }\n\n        void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.ViewHolder selected,\n                        List<RecoverAnimation> recoverAnimationList, float dX, float dY) {\n            final int recoverAnimSize = recoverAnimationList.size();\n            for (int i = 0; i < recoverAnimSize; i++) {\n                final RecoverAnimation anim = recoverAnimationList.get(i);\n                final int count = c.save();\n                onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, false);\n                c.restoreToCount(count);\n            }\n            if (selected != null) {\n                final int count = c.save();\n                onChildDrawOver(c, parent, selected, dX, dY, true);\n                c.restoreToCount(count);\n            }\n            boolean hasRunningAnimation = false;\n            for (int i = recoverAnimSize - 1; i >= 0; i--) {\n                final RecoverAnimation anim = recoverAnimationList.get(i);\n                if (anim.mEnded && !anim.mIsPendingCleanup) {\n                    recoverAnimationList.remove(i);\n                } else if (!anim.mEnded) {\n                    hasRunningAnimation = true;\n                }\n            }\n            if (hasRunningAnimation) {\n                parent.invalidate();\n            }\n        }\n\n        public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView,\n                                    @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY,\n                                    boolean isCurrentlyActive) {\n        }\n\n        protected boolean isOverThreshold(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dx, float dy, int swipeDirection) {\n            if (swipeDirection == SWIPE_LEFT || swipeDirection == SWIPE_RIGHT) {\n                return Math.abs(dx) >= recyclerView.getWidth() * getSwipeThreshold(viewHolder);\n            }\n            return Math.abs(dy) >= recyclerView.getHeight() * getSwipeThreshold(viewHolder);\n        }\n\n        public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,\n                                @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY,\n                                boolean isCurrentlyActive, int swipeDirection) {\n            View view = viewHolder.itemView;\n            view.setTranslationX(dX);\n            view.setTranslationY(dY);\n            if (viewHolder instanceof QMUISwipeViewHolder) {\n                if (swipeDirection != SWIPE_NONE) {\n                    ((QMUISwipeViewHolder) viewHolder).draw(c, isOverThreshold(recyclerView, viewHolder, dX, dY, swipeDirection), dX, dY);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/recyclerView/QMUISwipeAction.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.recyclerView;\n\nimport android.animation.TimeInterpolator;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.QMUIInterpolatorStaticHolder;\n\npublic class QMUISwipeAction {\n    final String mText;\n    Drawable mIcon;\n    int mTextSize;\n    Typeface mTypeface;\n    int mSwipeDirectionMiniSize;\n    int mIconTextGap;\n    int mTextColor;\n    int mTextColorAttr;\n    int mBackgroundColor;\n    int mBackgroundColorAttr;\n    int mIconAttr;\n    boolean mUseIconTint;\n    int mPaddingStartEnd;\n    int mOrientation;\n    boolean mReverseDrawOrder;\n    TimeInterpolator mSwipeMoveInterpolator;\n    int mSwipePxPerMS;\n\n\n    // inner use for layout and draw\n    Paint paint;\n    float contentWidth;\n    float contentHeight;\n\n\n    private QMUISwipeAction(ActionBuilder builder) {\n        mText = builder.mText != null && builder.mText.length() > 0 ? builder.mText : null;\n        mTextColor = builder.mTextColor;\n        mTextSize = builder.mTextSize;\n        mTypeface = builder.mTypeface;\n        mTextColorAttr = builder.mTextColorAttr;\n        mIcon = builder.mIcon;\n        mIconAttr = builder.mIconAttr;\n        mUseIconTint = builder.mUseIconTint;\n        mIconTextGap = builder.mIconTextGap;\n        mBackgroundColor = builder.mBackgroundColor;\n        mBackgroundColorAttr = builder.mBackgroundColorAttr;\n        mPaddingStartEnd = builder.mPaddingStartEnd;\n        mSwipeDirectionMiniSize = builder.mSwipeDirectionMiniSize;\n        mOrientation = builder.mOrientation;\n        mReverseDrawOrder = builder.mReverseDrawOrder;\n        mSwipeMoveInterpolator = builder.mSwipeMoveInterpolator;\n        mSwipePxPerMS = builder.mSwipePxPerMS;\n\n        paint = new Paint();\n        paint.setAntiAlias(true);\n        paint.setTypeface(mTypeface);\n        paint.setTextSize(mTextSize);\n        Paint.FontMetrics fontMetrics = paint.getFontMetrics();\n        if (mIcon != null && mText != null) {\n            mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());\n            if (mOrientation == ActionBuilder.HORIZONTAL) {\n                contentWidth = mIcon.getIntrinsicWidth() + mIconTextGap + paint.measureText(mText);\n                contentHeight = Math.max(fontMetrics.descent - fontMetrics.ascent, mIcon.getIntrinsicHeight());\n            } else {\n                contentWidth = Math.max(mIcon.getIntrinsicWidth(), paint.measureText(mText));\n                contentHeight = fontMetrics.descent - fontMetrics.ascent + mIconTextGap + mIcon.getIntrinsicHeight();\n            }\n        } else if (mIcon != null) {\n            mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());\n            contentWidth = mIcon.getIntrinsicWidth();\n            contentHeight = mIcon.getIntrinsicHeight();\n        } else if (mText != null) {\n            contentWidth = paint.measureText(mText);\n            contentHeight = fontMetrics.descent - fontMetrics.ascent;\n        }\n    }\n\n    public String getText() {\n        return mText;\n    }\n\n    public int getTextColor() {\n        return mTextColor;\n    }\n\n    public int getTextSize() {\n        return mTextSize;\n    }\n\n    public Typeface getTypeface() {\n        return mTypeface;\n    }\n\n    public int getTextColorAttr() {\n        return mTextColorAttr;\n    }\n\n    public Drawable getIcon() {\n        return mIcon;\n    }\n\n    public int getIconAttr() {\n        return mIconAttr;\n    }\n\n    public boolean isUseIconTint() {\n        return mUseIconTint;\n    }\n\n    public int getBackgroundColor() {\n        return mBackgroundColor;\n    }\n\n    public int getBackgroundColorAttr() {\n        return mBackgroundColorAttr;\n    }\n\n    public int getPaddingStartEnd() {\n        return mPaddingStartEnd;\n    }\n\n    public int getIconTextGap() {\n        return mIconTextGap;\n    }\n\n    public int getSwipeDirectionMiniSize() {\n        return mSwipeDirectionMiniSize;\n    }\n\n    public int getOrientation() {\n        return mOrientation;\n    }\n\n    protected void draw(Canvas canvas) {\n        if (mText != null && mIcon != null) {\n            if (mOrientation == ActionBuilder.HORIZONTAL) {\n                if (mReverseDrawOrder) {\n                    canvas.drawText(mText, 0,\n                            (contentHeight - paint.descent() + paint.ascent()) / 2 - paint.ascent(),\n                            paint);\n                    canvas.save();\n                    canvas.translate(contentWidth - mIcon.getIntrinsicWidth(), (contentHeight - mIcon.getIntrinsicHeight()) / 2);\n                    mIcon.draw(canvas);\n                    canvas.restore();\n                } else {\n                    canvas.save();\n                    canvas.translate(0, (contentHeight - mIcon.getIntrinsicHeight()) / 2);\n                    mIcon.draw(canvas);\n                    canvas.restore();\n                    canvas.drawText(mText,\n                            mIcon.getIntrinsicWidth() + mIconTextGap,\n                            (contentHeight - paint.descent() + paint.ascent()) / 2 - paint.ascent(),\n                            paint);\n                }\n\n            } else {\n                float textWidth = paint.measureText(mText);\n                if (mReverseDrawOrder) {\n                    canvas.drawText(mText, (contentWidth - textWidth) / 2, -paint.ascent(), paint);\n                    canvas.save();\n                    canvas.translate(\n                            (contentWidth - mIcon.getIntrinsicWidth()) / 2,\n                            contentHeight - mIcon.getIntrinsicHeight());\n                    mIcon.draw(canvas);\n                    canvas.restore();\n                } else {\n                    canvas.save();\n                    canvas.translate((contentWidth - mIcon.getIntrinsicWidth()) / 2, 0);\n                    mIcon.draw(canvas);\n                    canvas.restore();\n                    canvas.drawText(mText, (contentWidth - textWidth) / 2, contentHeight - paint.descent(), paint);\n                }\n            }\n        } else if (mIcon != null) {\n            mIcon.draw(canvas);\n        } else if (mText != null) {\n            canvas.drawText(mText, 0, -paint.ascent(), paint);\n        }\n\n    }\n\n    public static class ActionBuilder {\n        public static final int VERTICAL = 1;\n        public static final int HORIZONTAL = 2;\n        String mText;\n        Drawable mIcon;\n        int mTextSize;\n        Typeface mTypeface;\n        int mSwipeDirectionMiniSize;\n        int mIconTextGap;\n        int mTextColor;\n        int mTextColorAttr = 0;\n        int mBackgroundColor;\n        int mBackgroundColorAttr = 0;\n        int mIconAttr = 0;\n        boolean mUseIconTint = false;\n        int mPaddingStartEnd = 0;\n        int mOrientation = VERTICAL;\n        boolean mReverseDrawOrder = false;\n        TimeInterpolator mSwipeMoveInterpolator = QMUIInterpolatorStaticHolder.ACCELERATE_INTERPOLATOR;\n        int mSwipePxPerMS = 2;\n\n        public ActionBuilder text(String text) {\n            mText = text;\n            return this;\n        }\n\n        public ActionBuilder textSize(int textSize) {\n            mTextSize = textSize;\n            return this;\n        }\n\n        public ActionBuilder textColor(int textColor) {\n            mTextColor = textColor;\n            return this;\n        }\n\n        public ActionBuilder typeface(Typeface typeface) {\n            mTypeface = typeface;\n            return this;\n        }\n\n        public ActionBuilder textColorAttr(int textColorAttr) {\n            mTextColorAttr = textColorAttr;\n            return this;\n        }\n\n        public ActionBuilder icon(@Nullable Drawable drawable) {\n            mIcon = drawable == null ? null : drawable.mutate();\n            return this;\n        }\n\n        public ActionBuilder iconAttr(int iconAttr) {\n            mIconAttr = iconAttr;\n            return this;\n        }\n\n        public ActionBuilder useIconTint(boolean useIconTint) {\n            mUseIconTint = useIconTint;\n            return this;\n        }\n\n        public ActionBuilder backgroundColor(int backgroundColor) {\n            mBackgroundColor = backgroundColor;\n            return this;\n        }\n\n        public ActionBuilder backgroundColorAttr(int backgroundColorAttr) {\n            mBackgroundColorAttr = backgroundColorAttr;\n            return this;\n        }\n\n        public ActionBuilder paddingStartEnd(int paddingStartEnd) {\n            mPaddingStartEnd = paddingStartEnd;\n            return this;\n        }\n\n        public ActionBuilder iconTextGap(int iconTextGap) {\n            mIconTextGap = iconTextGap;\n            return this;\n        }\n\n        public ActionBuilder swipeDirectionMinSize(int minSize) {\n            mSwipeDirectionMiniSize = minSize;\n            return this;\n        }\n\n        public ActionBuilder orientation(int orientation) {\n            mOrientation = orientation;\n            return this;\n        }\n\n        public ActionBuilder reverseDrawOrder(boolean reverse) {\n            mReverseDrawOrder = reverse;\n            return this;\n        }\n\n        public ActionBuilder swipeMoveInterpolator(TimeInterpolator interpolator) {\n            mSwipeMoveInterpolator = interpolator;\n            return this;\n        }\n\n        public ActionBuilder swipePxPerMS(int swipePxPerMS){\n            mSwipePxPerMS = swipePxPerMS;\n            return this;\n        }\n\n        public QMUISwipeAction build() {\n            return new QMUISwipeAction(this);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/recyclerView/QMUISwipeViewHolder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.recyclerView;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.view.View;\nimport android.view.ViewParent;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction.SWIPE_DOWN;\nimport static com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction.SWIPE_LEFT;\nimport static com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction.SWIPE_NONE;\nimport static com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction.SWIPE_RIGHT;\nimport static com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction.SWIPE_UP;\n\npublic class QMUISwipeViewHolder extends RecyclerView.ViewHolder {\n\n    List<ActionWrapper> mSwipeActions;\n    int mActionTotalWidth = 0;\n    int mActionTotalHeight = 0;\n    int mSetupDirection = SWIPE_NONE;\n    ActionWrapper mCurrentTouchAction = null;\n    float mActionDownX = 0;\n    float mActionDownY = 0;\n    private QMUISwipeViewHolder.ActionWrapper.Callback mCallback = new ActionWrapper.Callback() {\n        @Override\n        public void invalidate() {\n            ViewParent viewParent = itemView.getParent();\n            if (viewParent instanceof RecyclerView) {\n                ((RecyclerView) viewParent).invalidate();\n            }\n        }\n    };\n\n    public QMUISwipeViewHolder(@NonNull View itemView) {\n        super(itemView);\n    }\n\n    public void addSwipeAction(QMUISwipeAction action) {\n        if (mSwipeActions == null) {\n            mSwipeActions = new ArrayList<>();\n        }\n        ActionWrapper actionWrapper = new ActionWrapper(action, mCallback);\n        mSwipeActions.add(actionWrapper);\n    }\n\n    public void clearActions(){\n        if(mSwipeActions != null){\n            mSwipeActions.clear();\n        }\n    }\n\n    public boolean hasAction() {\n        return mSwipeActions != null && !mSwipeActions.isEmpty();\n    }\n\n    public void clearTouchInfo() {\n        mCurrentTouchAction = null;\n        mActionDownY = -1;\n        mActionDownX = -1;\n    }\n\n    void setup(int swipeDirection, boolean swipeDeleteIfOnlyOneAction) {\n        mActionTotalWidth = 0;\n        mActionTotalHeight = 0;\n        if (mSwipeActions == null || mSwipeActions.isEmpty()) {\n            return;\n        }\n        mSetupDirection = swipeDirection;\n        for (ActionWrapper wrapper : mSwipeActions) {\n            QMUISwipeAction action = wrapper.action;\n            if (swipeDirection == SWIPE_LEFT || swipeDirection == SWIPE_RIGHT) {\n                wrapper.measureWidth = Math.max(action.mSwipeDirectionMiniSize,\n                        action.contentWidth + 2 * action.mPaddingStartEnd);\n                wrapper.measureHeight = itemView.getHeight();\n                mActionTotalWidth += wrapper.measureWidth;\n            } else if (swipeDirection == SWIPE_UP || swipeDirection == SWIPE_DOWN) {\n                wrapper.measureHeight = Math.max(action.mSwipeDirectionMiniSize,\n                        action.contentHeight + 2 * action.mPaddingStartEnd);\n                wrapper.measureWidth = itemView.getWidth();\n                mActionTotalHeight += wrapper.measureHeight;\n            }\n        }\n\n        if (mSwipeActions.size() == 1 && swipeDeleteIfOnlyOneAction) {\n            mSwipeActions.get(0).swipeDeleteMode = true;\n        } else {\n            for (ActionWrapper wrapper : mSwipeActions) {\n                wrapper.swipeDeleteMode = false;\n            }\n        }\n\n        if (swipeDirection == SWIPE_LEFT) {\n            int targetLeft = itemView.getRight() - mActionTotalWidth;\n            for (ActionWrapper wrapper : mSwipeActions) {\n                wrapper.initLeft = itemView.getRight();\n                wrapper.initTop = wrapper.targetTop = itemView.getTop();\n                wrapper.targetLeft = targetLeft;\n                targetLeft += wrapper.measureWidth;\n            }\n        } else if (swipeDirection == SWIPE_RIGHT) {\n            int targetLeft = 0;\n            for (ActionWrapper wrapper : mSwipeActions) {\n                wrapper.initLeft = itemView.getLeft() - wrapper.measureWidth;\n                wrapper.initTop = wrapper.targetTop = itemView.getTop();\n                wrapper.targetLeft = targetLeft;\n                targetLeft += wrapper.measureWidth;\n            }\n        } else if (swipeDirection == SWIPE_UP) {\n            int targetTop = itemView.getBottom() - mActionTotalHeight;\n            for (ActionWrapper wrapper : mSwipeActions) {\n                wrapper.initLeft = wrapper.targetLeft = itemView.getLeft();\n                wrapper.initTop = itemView.getBottom();\n                wrapper.targetTop = targetTop;\n                targetTop += wrapper.measureHeight;\n            }\n        } else if (swipeDirection == SWIPE_DOWN) {\n            int targetTop = 0;\n            for (ActionWrapper wrapper : mSwipeActions) {\n                wrapper.initLeft = wrapper.targetLeft = itemView.getLeft();\n                wrapper.initTop = itemView.getTop() - wrapper.measureHeight;\n                wrapper.targetTop = targetTop;\n                targetTop += wrapper.measureHeight;\n            }\n        }\n    }\n\n    boolean checkDown(float x, float y) {\n        for (ActionWrapper actionInfo : mSwipeActions) {\n            if (actionInfo.hitTest(x, y)) {\n                mCurrentTouchAction = actionInfo;\n                mActionDownX = x;\n                mActionDownY = y;\n                return true;\n            }\n        }\n        return false;\n    }\n\n    QMUISwipeAction checkUp(float x, float y, int touchSlop) {\n        if (mCurrentTouchAction != null && mCurrentTouchAction.hitTest(x, y)) {\n            if (Math.abs(x - mActionDownX) < touchSlop && Math.abs(y - mActionDownY) < touchSlop) {\n                return mCurrentTouchAction.action;\n            }\n        }\n        return null;\n    }\n\n    void draw(Canvas canvas, boolean overSwipeThreshold, float dx, float dy) {\n        if (mSwipeActions == null || mSwipeActions.isEmpty()) {\n            return;\n        }\n        if (mActionTotalWidth > 0) {\n            float absDx = Math.abs(dx);\n            if (absDx <= mActionTotalWidth) {\n                float percent = absDx / mActionTotalWidth;\n                for (ActionWrapper actionInfo : mSwipeActions) {\n                    actionInfo.width = actionInfo.measureWidth;\n                    actionInfo.left = actionInfo.initLeft + (actionInfo.targetLeft - actionInfo.initLeft) * percent;\n                }\n            } else {\n                float overDx = absDx - mActionTotalWidth;\n                float eachOver = overDx / mSwipeActions.size();\n                float startLeft = dx > 0 ? itemView.getLeft() : itemView.getRight() + dx;\n                for (ActionWrapper actionInfo : mSwipeActions) {\n                    actionInfo.width = actionInfo.measureWidth + eachOver;\n                    actionInfo.left = startLeft;\n                    startLeft += actionInfo.width;\n                }\n            }\n        } else {\n            for (ActionWrapper actionInfo : mSwipeActions) {\n                actionInfo.width = actionInfo.measureWidth;\n                actionInfo.left = actionInfo.initLeft;\n            }\n        }\n        if (mActionTotalHeight > 0) {\n            float absDy = Math.abs(dy);\n            if (absDy <= mActionTotalHeight) {\n                float percent = absDy / mActionTotalHeight;\n                for (ActionWrapper actionInfo : mSwipeActions) {\n                    actionInfo.height = actionInfo.measureHeight;\n                    actionInfo.top = actionInfo.initTop + (actionInfo.targetTop - actionInfo.initTop) * percent;\n                }\n            } else {\n                float overDy = absDy - mActionTotalHeight;\n                float eachOver = overDy / mSwipeActions.size();\n                float startTop = dy > 0 ? itemView.getTop() : itemView.getBottom() + dy;\n                for (ActionWrapper actionInfo : mSwipeActions) {\n                    actionInfo.height = actionInfo.measureHeight + eachOver + 0.5f;\n                    actionInfo.top = startTop;\n                    startTop += actionInfo.height;\n                }\n            }\n        } else {\n            for (ActionWrapper actionInfo : mSwipeActions) {\n                actionInfo.height = actionInfo.measureHeight;\n                actionInfo.top = actionInfo.initTop;\n            }\n        }\n        for (ActionWrapper actionInfo : mSwipeActions) {\n            actionInfo.draw(canvas, overSwipeThreshold, mSetupDirection);\n        }\n    }\n\n    static class ActionWrapper {\n        static int SWIPE_DELETE_BEFORE = 0;\n        static int SWIPE_DELETE_ANIMATING_TO_AFTER = 1;\n        static int SWIPE_DELETE_ANIMATING_TO_BEFORE = 2;\n        static int SWIPE_DELETE_AFTER = 3;\n        static int MAX_SWIPE_MOVE_DURATION = 250;\n        final QMUISwipeAction action;\n        final Callback callback;\n\n        float measureWidth;\n        float measureHeight;\n        float targetLeft;\n        float targetTop;\n        float initLeft;\n        float initTop;\n        float left;\n        float top;\n        float width;\n        float height;\n\n        boolean swipeDeleteMode = false;\n        private int swipeDeleteState = SWIPE_DELETE_BEFORE;\n        private float currentAnimationProgress = 0;\n        private ValueAnimator animator;\n        private ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                currentAnimationProgress = (float) animation.getAnimatedValue();\n                callback.invalidate();\n            }\n        };\n        private float lastLeft = -1, lastTop = -1, animStartLeft = -1, animStartTop = -1;\n\n        public ActionWrapper(@NonNull QMUISwipeAction action, @NonNull Callback callback) {\n            this.action = action;\n            this.callback = callback;\n        }\n\n        boolean hitTest(float x, float y) {\n            return x > left && x < left + width && y > top && y < top + height;\n        }\n\n        void draw(Canvas canvas, boolean overSwipeThreshold, int direction) {\n            canvas.save();\n            canvas.translate(left, top);\n            action.paint.setStyle(Paint.Style.FILL);\n            action.paint.setColor(action.mBackgroundColor);\n            canvas.drawRect(0, 0, width, height, action.paint);\n            if (!swipeDeleteMode) {\n                canvas.translate((width - action.contentWidth) / 2f, (height - action.contentHeight) / 2);\n            } else {\n                float anchorLeft = getAnchorDrawLeft(direction);\n                float anchorTop = getAnchorDrawTop(direction);\n                float followLeft = getFollowDrawLeft(direction);\n                float followTop = getFollowDrawTop(direction);\n                float drawLeft, drawTop;\n                if (!overSwipeThreshold) {\n                    if (swipeDeleteState == SWIPE_DELETE_BEFORE) {\n                        drawLeft = anchorLeft;\n                        drawTop = anchorTop;\n                    } else if (swipeDeleteState == SWIPE_DELETE_AFTER) {\n                        swipeDeleteState = SWIPE_DELETE_ANIMATING_TO_BEFORE;\n                        drawLeft = followLeft;\n                        drawTop = followTop;\n                        startAnimator(drawLeft, drawTop, anchorLeft, anchorTop, direction);\n                    } else if (swipeDeleteState == SWIPE_DELETE_ANIMATING_TO_AFTER) {\n                        swipeDeleteState = SWIPE_DELETE_ANIMATING_TO_BEFORE;\n                        drawLeft = lastLeft;\n                        drawTop = lastTop;\n                        startAnimator(drawLeft, drawTop, anchorLeft, anchorTop, direction);\n                    } else {\n                        if (isVer(direction)) {\n                            drawLeft = anchorLeft;\n                            drawTop = animStartTop + (anchorTop - animStartTop) * currentAnimationProgress;\n                        } else {\n                            drawLeft = animStartLeft + (anchorLeft - animStartLeft) * currentAnimationProgress;\n                            drawTop = anchorTop;\n                        }\n                        if (currentAnimationProgress >= 1f) {\n                            swipeDeleteState = SWIPE_DELETE_BEFORE;\n                        }\n                    }\n                } else {\n                    if (swipeDeleteState == SWIPE_DELETE_AFTER) {\n                        drawLeft = followLeft;\n                        drawTop = followTop;\n                    } else if (swipeDeleteState == SWIPE_DELETE_ANIMATING_TO_BEFORE) {\n                        swipeDeleteState = SWIPE_DELETE_ANIMATING_TO_AFTER;\n                        drawLeft = lastLeft;\n                        drawTop = lastTop;\n                        startAnimator(drawLeft, drawTop, followLeft, followTop, direction);\n                    } else if (swipeDeleteState == SWIPE_DELETE_BEFORE) {\n                        swipeDeleteState = SWIPE_DELETE_ANIMATING_TO_AFTER;\n                        drawLeft = anchorLeft;\n                        drawTop = anchorTop;\n                        startAnimator(drawLeft, drawTop, followLeft, followTop, direction);\n                    } else {\n                        if (isVer(direction)) {\n                            drawLeft = followLeft;\n                            drawTop = animStartTop + (followTop - animStartTop) * currentAnimationProgress;\n                        } else {\n                            drawLeft = animStartLeft + (followLeft - animStartLeft) * currentAnimationProgress;\n                            drawTop = followTop;\n                        }\n                        if (currentAnimationProgress >= 1f) {\n                            swipeDeleteState = SWIPE_DELETE_AFTER;\n                        }\n                    }\n                }\n                canvas.translate(drawLeft - left, drawTop - top);\n                lastLeft = drawLeft;\n                lastTop = drawTop;\n            }\n            action.paint.setColor(action.mTextColor);\n            action.draw(canvas);\n            canvas.restore();\n        }\n\n        private void startAnimator(float curLeft, float curTop, float targetLeft, float targetTop, int direction) {\n            QMUIViewHelper.clearValueAnimator(animator);\n            if (isVer(direction)) {\n                animator = ValueAnimator.ofFloat(0, 1);\n                animStartTop = curTop;\n            } else {\n                animator = ValueAnimator.ofFloat(0, 1);\n                animStartLeft = curLeft;\n            }\n            float dis = isVer(direction) ? Math.abs(targetTop - curTop) : Math.abs(targetLeft - curLeft);\n            int duration = Math.min(MAX_SWIPE_MOVE_DURATION, (int) (dis / action.mSwipePxPerMS));\n            animator.setDuration(duration);\n            animator.setInterpolator(action.mSwipeMoveInterpolator);\n            animator.addUpdateListener(listener);\n            animator.start();\n        }\n\n        private boolean isVer(int direction) {\n            return direction == SWIPE_DOWN || direction == SWIPE_UP;\n        }\n\n        private float getAnchorDrawLeft(int direction) {\n            if(direction == SWIPE_LEFT){\n                if(left > targetLeft){\n                    return getFollowDrawLeft(direction);\n                }\n            }else if(direction == SWIPE_RIGHT){\n                if(left < targetLeft){\n                    return getFollowDrawLeft(direction);\n                }\n            }\n            return targetLeft + (measureWidth - action.contentWidth) / 2;\n        }\n\n        private float getAnchorDrawTop(int direction) {\n            if(direction == SWIPE_UP){\n                if(top > targetTop){\n                    return getFollowDrawTop(direction);\n                }\n            }else if(direction == SWIPE_DOWN){\n                if(top < targetTop){\n                    return getFollowDrawTop(direction);\n                }\n            }\n            return targetTop + (measureHeight - action.contentHeight) / 2;\n        }\n\n        private float getFollowDrawLeft(int direction) {\n            float innerHor = (measureWidth - action.contentWidth) / 2;\n            if (direction == SWIPE_LEFT) {\n                return left + innerHor;\n            } else if (direction == SWIPE_RIGHT) {\n                return left + width - measureWidth + innerHor;\n            }\n            return left + (width - action.contentWidth) / 2f;\n        }\n\n        private float getFollowDrawTop(int direction) {\n            float innerVer = (measureHeight - action.contentHeight) / 2;\n            if (direction == SWIPE_UP) {\n                return top + innerVer;\n            } else if (direction == SWIPE_DOWN) {\n                return top + height - measureHeight + innerVer;\n            }\n            return top + (height - action.contentHeight) / 2f;\n        }\n\n        interface Callback {\n            void invalidate();\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/IQMUISkinApplyListener.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\npublic interface IQMUISkinApplyListener {\n    void onApply(View view, int skinIndex, @NonNull Resources.Theme theme);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/IQMUISkinDispatchInterceptor.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.Resources;\n\nimport androidx.annotation.NonNull;\n\npublic interface IQMUISkinDispatchInterceptor {\n    boolean intercept(int skinIndex, @NonNull Resources.Theme theme);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/IQMUISkinHandlerDecoration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.Resources;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic interface IQMUISkinHandlerDecoration {\n\n    void handle(@NonNull RecyclerView recyclerView, @NonNull QMUISkinManager manager,\n                int skinIndex, @NonNull Resources.Theme theme);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/IQMUISkinHandlerSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic interface IQMUISkinHandlerSpan {\n\n    void handle(@NonNull View view, @NonNull QMUISkinManager manager, int skinIndex, @NonNull Resources.Theme theme);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/IQMUISkinHandlerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.Resources;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\npublic interface IQMUISkinHandlerView {\n    void handle(@NonNull QMUISkinManager manager,\n                int skinIndex,\n                @NonNull Resources.Theme theme,\n                @Nullable SimpleArrayMap<String, Integer> attrs);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.skin;\n\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\n\nimport androidx.annotation.MainThread;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\npublic class QMUISkinHelper {\n\n    public static QMUISkinValueBuilder sSkinValueBuilder = QMUISkinValueBuilder.acquire();\n\n    public static Resources.Theme getSkinTheme(@NonNull View view) {\n        QMUISkinManager.ViewSkinCurrent current = QMUISkinManager.getViewSkinCurrent(view);\n        Resources.Theme theme;\n        if (current == null || current.index < 0) {\n            theme = view.getContext().getTheme();\n        } else {\n            theme = QMUISkinManager.of(current.managerName, view.getContext()).getTheme(current.index);\n        }\n        return theme;\n    }\n\n    public static int getSkinColor(@NonNull View view, int colorAttr) {\n        return QMUIResHelper.getAttrColor(getSkinTheme(view), colorAttr);\n    }\n\n    public static ColorStateList getSkinColorStateList(@NonNull View view, int colorAttr) {\n        return QMUIResHelper.getAttrColorStateList(view.getContext(), getSkinTheme(view), colorAttr);\n    }\n\n    @Nullable\n    public static Drawable getSkinDrawable(@NonNull View view, int drawableAttr) {\n        return QMUIResHelper.getAttrDrawable(view.getContext(), getSkinTheme(view), drawableAttr);\n    }\n\n\n    public static void setSkinValue(@NonNull View view, QMUISkinValueBuilder skinValueBuilder) {\n        setSkinValue(view, skinValueBuilder.build());\n    }\n\n    public static void setSkinValue(@NonNull View view, String value) {\n        view.setTag(R.id.qmui_skin_value, value);\n        refreshViewSkin(view);\n\n    }\n\n    @MainThread\n    public static void setSkinValue(@NonNull View view, SkinWriter writer) {\n        writer.write(sSkinValueBuilder);\n        setSkinValue(view, sSkinValueBuilder.build());\n        sSkinValueBuilder.clear();\n    }\n\n    public static void refreshRVItemDecoration(@NonNull RecyclerView view, IQMUISkinHandlerDecoration itemDecoration) {\n        QMUISkinManager.ViewSkinCurrent skinCurrent = QMUISkinManager.getViewSkinCurrent(view);\n        if (skinCurrent != null) {\n            QMUISkinManager.of(skinCurrent.managerName, view.getContext()).refreshRecyclerDecoration(view, itemDecoration, skinCurrent.index);\n        }\n    }\n\n    public static int getCurrentSkinIndex(@NonNull View view) {\n        QMUISkinManager.ViewSkinCurrent viewSkinCurrent = QMUISkinManager.getViewSkinCurrent(view);\n        if (viewSkinCurrent != null) {\n            return viewSkinCurrent.index;\n        }\n        return QMUISkinManager.DEFAULT_SKIN;\n    }\n\n    public static void refreshViewSkin(@NonNull View view) {\n        QMUISkinManager.ViewSkinCurrent skinCurrent = QMUISkinManager.getViewSkinCurrent(view);\n        if (skinCurrent != null) {\n            QMUISkinManager.of(skinCurrent.managerName, view.getContext()).refreshTheme(view, skinCurrent.index);\n        }\n    }\n\n    public static void syncViewSkin(@NonNull View view, @NonNull View sourceView) {\n        QMUISkinManager.ViewSkinCurrent source = QMUISkinManager.getViewSkinCurrent(sourceView);\n        if (source != null) {\n            QMUISkinManager.ViewSkinCurrent skin = QMUISkinManager.getViewSkinCurrent(view);\n            if (!source.equals(skin)) {\n                QMUISkinManager.of(source.managerName, view.getContext()).dispatch(view, source.index);\n            }\n        }\n    }\n\n    public static void setSkinDefaultProvider(@NonNull View view,\n                                              IQMUISkinDefaultAttrProvider provider) {\n        view.setTag(R.id.qmui_skin_default_attr_provider, provider);\n    }\n\n    public static void setSkinApplyListener(@NonNull View view, @Nullable IQMUISkinApplyListener listener) {\n        view.setTag(R.id.qmui_skin_apply_listener, listener);\n    }\n\n    @Nullable\n    public static IQMUISkinApplyListener getSkinApplyListener(@NonNull View view) {\n        Object listener = view.getTag(R.id.qmui_skin_apply_listener);\n        if (listener instanceof IQMUISkinApplyListener) {\n            return (IQMUISkinApplyListener) listener;\n        }\n        return null;\n    }\n\n    public static void setIgnoreSkinApply(@NonNull View view, boolean ignore){\n        view.setTag(R.id.qmui_skin_ignore_apply, ignore);\n    }\n\n    public static void setInterceptSkinDispatch(@NonNull View view, boolean intercept){\n        view.setTag(R.id.qmui_skin_intercept_dispatch, intercept);\n    }\n\n    public static void warnRuleNotSupport(View view, String rule) {\n        QMUILog.w(\"QMUISkinManager\",\n                view.getClass().getSimpleName() + \" does't support \" + rule);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinLayoutInflaterFactory.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.InflateException;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\n\npublic class QMUISkinLayoutInflaterFactory implements LayoutInflater.Factory2 {\n    private static final String TAG = \"QMUISkin\";\n    private static final String[] sClassPrefixList = {\n            \"android.widget.\",\n            \"android.webkit.\",\n            \"android.app.\",\n            \"android.view.\"\n    };\n    private static final HashMap<String, String> sSuccessClassNamePrefixMap = new HashMap<>();\n\n    /**\n     * LayoutInflater.createView(four args) is provided in Android P, but some ROM did't follow the official.\n     */\n    private static boolean sCanUseCreateViewFourArguments = true;\n    private static boolean sDidCheckLayoutInflaterCreateViewExitFourArgMethod = false;\n\n    private Resources.Theme mEmptyTheme;\n    private WeakReference<Activity> mActivityWeakReference;\n    private LayoutInflater mOriginLayoutInflater;\n\n    public QMUISkinLayoutInflaterFactory(Activity activity, LayoutInflater originLayoutInflater) {\n        mActivityWeakReference = new WeakReference<>(activity);\n        mOriginLayoutInflater = originLayoutInflater;\n    }\n\n    public QMUISkinLayoutInflaterFactory cloneForLayoutInflaterIfNeeded(LayoutInflater layoutInflater){\n        if(mOriginLayoutInflater.getContext() == layoutInflater.getContext()){\n            return this;\n        }\n        return new QMUISkinLayoutInflaterFactory(mActivityWeakReference.get(), layoutInflater);\n    }\n\n    @Override\n    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {\n        Activity activity = mActivityWeakReference.get();\n        View view = null;\n        if(activity instanceof AppCompatActivity){\n            view = ((AppCompatActivity)activity).getDelegate().createView(parent, name, context, attrs);\n        }\n\n        if(view == null){\n            try{\n                if (!name.contains(\".\")) {\n                    if(sSuccessClassNamePrefixMap.containsKey(name)){\n                        view = mOriginLayoutInflater\n                                .createView(name, sSuccessClassNamePrefixMap.get(name), attrs);\n                    }else{\n                        for (String prefix : sClassPrefixList) {\n                            try {\n                                view = mOriginLayoutInflater.createView(name, prefix, attrs);\n                                if (view != null) {\n                                    sSuccessClassNamePrefixMap.put(name, prefix);\n                                    break;\n                                }\n                            } catch (Exception ignored) {\n                            }\n                        }\n                    }\n                }else{\n                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){\n                        if(!sDidCheckLayoutInflaterCreateViewExitFourArgMethod){\n                            try{\n                                LayoutInflater.class.getDeclaredMethod(\n                                        \"createView\", Context.class, String.class, String.class, AttributeSet.class);\n                            }catch (Exception e){\n                                sCanUseCreateViewFourArguments = false;\n                            }\n                            sDidCheckLayoutInflaterCreateViewExitFourArgMethod = true;\n                        }\n                        if(sCanUseCreateViewFourArguments){\n                            view = mOriginLayoutInflater.createView(context, name, null, attrs);\n                        }else{\n                            view = originCreateViewForLowSDK(name, context, attrs);\n                        }\n                    }else{\n                       view = originCreateViewForLowSDK(name, context, attrs);\n                    }\n                }\n            }catch (ClassNotFoundException ignore){\n\n            }catch (Exception e){\n                QMUILog.e(TAG, \"Failed to inflate view \" + name + \"; error: \" + e.getMessage());\n            }\n        }\n\n        if (view != null) {\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            getSkinValueFromAttributeSet(view.getContext(), attrs, builder);\n            if (!builder.isEmpty()) {\n                QMUISkinHelper.setSkinValue(view, builder);\n            }\n            QMUISkinValueBuilder.release(builder);\n        }\n\n        return view;\n    }\n\n    private View originCreateViewForLowSDK(String name, Context context, AttributeSet attrs)\n            throws NoSuchFieldException, IllegalArgumentException,\n            IllegalAccessException, InflateException, ClassNotFoundException {\n        @SuppressLint(\"SoonBlockedPrivateApi\") Field field = LayoutInflater.class.getDeclaredField(\"mConstructorArgs\");\n        field.setAccessible(true);\n        Object[] mConstructorArgs = (Object[]) field.get(mOriginLayoutInflater);\n        Object lastContext = mConstructorArgs[0];\n        mConstructorArgs[0] = context;\n        View view = mOriginLayoutInflater.createView(name, null, attrs);\n        mConstructorArgs[0] = lastContext;\n        return view;\n    }\n\n    @Override\n    public View onCreateView(String name, Context context, AttributeSet attrs) {\n        return onCreateView(null, name, context, attrs);\n    }\n\n    public void getSkinValueFromAttributeSet(Context context, @Nullable AttributeSet attrs, QMUISkinValueBuilder builder) {\n        // use a empty theme, so we can get the attr's own value, not it's ref value\n        if(mEmptyTheme == null){\n            mEmptyTheme = context.getApplicationContext().getResources().newTheme();\n        }\n        TypedArray a = mEmptyTheme.obtainStyledAttributes(attrs, R.styleable.QMUISkinDef, 0, 0);\n        int count = a.getIndexCount();\n        for (int i = 0; i < count; i++) {\n            int attr = a.getIndex(i);\n            String name = a.getString(attr);\n            if (QMUILangHelper.isNullOrEmpty(name)) {\n                continue;\n            }\n            if (name.startsWith(\"?\")) {\n                name = name.substring(1);\n            }\n            int id = context.getResources().getIdentifier(\n                    name, \"attr\", context.getPackageName());\n            if (id == 0) {\n                continue;\n            }\n            if (attr == R.styleable.QMUISkinDef_qmui_skin_background) {\n                builder.background(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_alpha) {\n                builder.alpha(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_border) {\n                builder.border(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_text_color) {\n                builder.textColor(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_second_text_color) {\n                builder.secondTextColor(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_src) {\n                builder.src(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_tint_color) {\n                builder.tintColor(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_separator_top) {\n                builder.topSeparator(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_separator_right) {\n                builder.rightSeparator(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_separator_bottom) {\n                builder.bottomSeparator(id);\n            } else if (attr == R.styleable.QMUISkinDef_qmui_skin_separator_left) {\n                builder.leftSeparator(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_bg_tint_color) {\n                builder.bgTintColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_progress_color){\n                builder.progressColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_underline){\n                builder.underline(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_more_bg_color){\n                builder.moreBgColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_more_text_color){\n                builder.moreTextColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_hint_color){\n                builder.hintColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_text_compound_tint_color){\n                builder.textCompoundTintColor(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_text_compound_src_left){\n                builder.textCompoundLeftSrc(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_text_compound_src_top){\n                builder.textCompoundTopSrc(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_text_compound_src_right){\n                builder.textCompoundRightSrc(id);\n            }else if(attr == R.styleable.QMUISkinDef_qmui_skin_text_compound_src_bottom){\n                builder.textCompoundBottomSrc(id);\n            }\n        }\n        a.recycle();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.os.Trace;\nimport android.text.Spanned;\nimport android.util.ArrayMap;\nimport android.util.SparseArray;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.AdapterView;\nimport android.widget.PopupWindow;\nimport android.widget.TextView;\n\nimport androidx.annotation.MainThread;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.viewpager.widget.ViewPager;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.annotation.QMUISkinListenWithHierarchyChange;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.skin.handler.IQMUISkinRuleHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleAlphaHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleBackgroundHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleBgTintColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleBorderHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleHintColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleMoreBgColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleMoreTextColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleProgressColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleSeparatorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleSrcHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleTextColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleTextCompoundSrcHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleTextCompoundTintColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleTintColorHandler;\nimport com.qmuiteam.qmui.skin.handler.QMUISkinRuleUnderlineHandler;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Objects;\n\npublic final class QMUISkinManager {\n    private static final String TAG = \"QMUISkinManager\";\n    public static final int DEFAULT_SKIN = -1;\n    private static final String[] EMPTY_ITEMS = new String[]{};\n    private static ArrayMap<String, QMUISkinManager> sInstances = new ArrayMap<>();\n    private static final String DEFAULT_NAME = \"default\";\n    public static final DispatchListenStrategySelector DEFAULT_DISPATCH_LISTEN_STRATEGY_SELECTOR = new DispatchListenStrategySelector() {\n        @NonNull\n        @Override\n        public DispatchListenStrategy select(@NonNull ViewGroup viewGroup) {\n            if (viewGroup instanceof RecyclerView ||\n                    viewGroup instanceof ViewPager ||\n                    viewGroup instanceof AdapterView ||\n                    viewGroup.getClass().isAnnotationPresent(QMUISkinListenWithHierarchyChange.class)) {\n                return DispatchListenStrategy.LISTEN_ON_HIERARCHY_CHANGE;\n            }\n            return DispatchListenStrategy.LISTEN_ON_LAYOUT;\n        }\n    };\n    private static DispatchListenStrategySelector sDispatchListenStrategySelector = DEFAULT_DISPATCH_LISTEN_STRATEGY_SELECTOR;\n\n    public static void setDispatchListenStrategySelector(DispatchListenStrategySelector dispatchListenStrategySelector) {\n        if (dispatchListenStrategySelector == null) {\n            sDispatchListenStrategySelector = DEFAULT_DISPATCH_LISTEN_STRATEGY_SELECTOR;\n        } else {\n            sDispatchListenStrategySelector = dispatchListenStrategySelector;\n        }\n    }\n\n    @MainThread\n    public static QMUISkinManager defaultInstance(Context context) {\n        context = context.getApplicationContext();\n        return of(DEFAULT_NAME, context.getResources(), context.getPackageName());\n    }\n\n    @MainThread\n    public static QMUISkinManager of(String name, Resources resources, String packageName) {\n        QMUISkinManager instance = sInstances.get(name);\n        if (instance == null) {\n            instance = new QMUISkinManager(name, resources, packageName);\n            sInstances.put(name, instance);\n        }\n        return instance;\n    }\n\n    @MainThread\n    public static QMUISkinManager of(String name, Context context) {\n        context = context.getApplicationContext();\n        return of(name, context.getResources(), context.getPackageName());\n    }\n\n\n    //==============================================================================================\n\n    private String mName;\n    private Resources mResources;\n    private String mPackageName;\n    private SparseArray<SkinItem> mSkins = new SparseArray<>();\n    private static HashMap<String, IQMUISkinRuleHandler> sRuleHandlers = new HashMap<>();\n    private static HashMap<Integer, Resources.Theme> sStyleIdThemeMap = new HashMap<>();\n    private boolean mIsInSkinChangeDispatch = false;\n\n    static {\n        sRuleHandlers.put(QMUISkinValueBuilder.BACKGROUND, new QMUISkinRuleBackgroundHandler());\n        IQMUISkinRuleHandler textColorHandler = new QMUISkinRuleTextColorHandler();\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COLOR, textColorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.SECOND_TEXT_COLOR, textColorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.SRC, new QMUISkinRuleSrcHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.BORDER, new QMUISkinRuleBorderHandler());\n        IQMUISkinRuleHandler separatorHandler = new QMUISkinRuleSeparatorHandler();\n        sRuleHandlers.put(QMUISkinValueBuilder.TOP_SEPARATOR, separatorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.RIGHT_SEPARATOR, separatorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.BOTTOM_SEPARATOR, separatorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.LEFT_SEPARATOR, separatorHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.TINT_COLOR, new QMUISkinRuleTintColorHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.ALPHA, new QMUISkinRuleAlphaHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.BG_TINT_COLOR, new QMUISkinRuleBgTintColorHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.PROGRESS_COLOR, new QMUISkinRuleProgressColorHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COMPOUND_TINT_COLOR, new QMUISkinRuleTextCompoundTintColorHandler());\n        IQMUISkinRuleHandler textCompoundSrcHandler = new QMUISkinRuleTextCompoundSrcHandler();\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COMPOUND_LEFT_SRC, textCompoundSrcHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COMPOUND_TOP_SRC, textCompoundSrcHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COMPOUND_RIGHT_SRC, textCompoundSrcHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.TEXT_COMPOUND_BOTTOM_SRC, textCompoundSrcHandler);\n        sRuleHandlers.put(QMUISkinValueBuilder.HINT_COLOR, new QMUISkinRuleHintColorHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.UNDERLINE, new QMUISkinRuleUnderlineHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.MORE_TEXT_COLOR, new QMUISkinRuleMoreTextColorHandler());\n        sRuleHandlers.put(QMUISkinValueBuilder.MORE_BG_COLOR, new QMUISkinRuleMoreBgColorHandler());\n    }\n\n    public static void setRuleHandler(String name, IQMUISkinRuleHandler handler) {\n        sRuleHandlers.put(name, handler);\n    }\n\n    // Actually, ViewGroup.OnHierarchyChangeListener is a better choice, but it only has a setter.\n    // Add child will trigger onLayoutChange\n    private static View.OnLayoutChangeListener mOnLayoutChangeListener = new View.OnLayoutChangeListener() {\n\n        @Override\n        public void onLayoutChange(\n                View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {\n            if (v instanceof ViewGroup) {\n                ViewGroup viewGroup = (ViewGroup) v;\n                int childCount = viewGroup.getChildCount();\n                if (childCount > 0) {\n                    ViewSkinCurrent current = getViewSkinCurrent(viewGroup);\n                    if (current != null) {\n                        View child;\n                        for (int i = 0; i < childCount; i++) {\n                            child = viewGroup.getChildAt(i);\n                            ViewSkinCurrent childTheme = getViewSkinCurrent(child);\n                            if (!current.equals(childTheme)) {\n                                of(current.managerName, child.getContext()).dispatch(child, current.index);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    };\n\n\n    private static ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener = new ViewGroup.OnHierarchyChangeListener() {\n        @Override\n        public void onChildViewAdded(View parent, View child) {\n            ViewSkinCurrent current = getViewSkinCurrent(parent);\n            if (current != null) {\n                ViewSkinCurrent childTheme = getViewSkinCurrent(child);\n                if (!current.equals(childTheme)) {\n                    of(current.managerName, child.getContext()).dispatch(child, current.index);\n                }\n            }\n        }\n\n        @Override\n        public void onChildViewRemoved(View parent, View child) {\n\n        }\n    };\n\n\n    public QMUISkinManager(String name, Resources resources, String packageName) {\n        mName = name;\n        mResources = resources;\n        mPackageName = packageName;\n    }\n\n    public String getName() {\n        return mName;\n    }\n\n    @Nullable\n    public Resources.Theme getTheme(int skinIndex) {\n        SkinItem skinItem = mSkins.get(skinIndex);\n        if (skinItem != null) {\n            return skinItem.getTheme();\n        }\n        return null;\n    }\n\n    @Nullable\n    public Resources.Theme getCurrentTheme() {\n        SkinItem skinItem = mSkins.get(mCurrentSkin);\n        if (skinItem != null) {\n            return skinItem.getTheme();\n        }\n        return null;\n    }\n\n    @MainThread\n    public void addSkin(int index, int styleRes) {\n        if (index <= 0) {\n            throw new IllegalArgumentException(\"index must greater than 0\");\n        }\n        SkinItem skinItem = mSkins.get(index);\n        if (skinItem != null) {\n            if (skinItem.getStyleRes() == styleRes) {\n                return;\n            }\n            throw new RuntimeException(\"already exist the theme item for \" + index);\n        }\n        skinItem = new SkinItem(styleRes);\n        mSkins.append(index, skinItem);\n    }\n\n    static ViewSkinCurrent getViewSkinCurrent(View view) {\n        Object current = view.getTag(R.id.qmui_skin_current);\n        if (current instanceof ViewSkinCurrent) {\n            return (ViewSkinCurrent) current;\n        }\n        return null;\n    }\n\n    public void dispatch(View view, int skinIndex) {\n        if (view == null) {\n            return;\n        }\n        if (QMUIConfig.DEBUG) {\n            Trace.beginSection(\"QMUISkin::dispatch\");\n        }\n        SkinItem skinItem = mSkins.get(skinIndex);\n        Resources.Theme theme;\n        if (skinItem == null) {\n            if (skinIndex != DEFAULT_SKIN) {\n                throw new IllegalArgumentException(\"The skin \" + skinIndex + \" does not exist\");\n            }\n            theme = view.getContext().getTheme();\n        } else {\n            theme = skinItem.getTheme();\n        }\n        runDispatch(view, skinIndex, theme);\n        if (QMUIConfig.DEBUG) {\n            Trace.endSection();\n        }\n    }\n\n\n    private void runDispatch(@NonNull View view, int skinIndex, Resources.Theme theme) {\n        ViewSkinCurrent currentTheme = getViewSkinCurrent(view);\n        if (currentTheme != null && currentTheme.index == skinIndex && Objects.equals(currentTheme.managerName, mName)) {\n            return;\n        }\n        view.setTag(R.id.qmui_skin_current, new ViewSkinCurrent(mName, skinIndex));\n\n        if (view instanceof IQMUISkinDispatchInterceptor) {\n            if (((IQMUISkinDispatchInterceptor) view).intercept(skinIndex, theme)) {\n                return;\n            }\n        }\n\n        Object interceptTag = view.getTag(R.id.qmui_skin_intercept_dispatch);\n        if (interceptTag instanceof Boolean && ((Boolean) interceptTag)) {\n            return;\n        }\n\n        Object ignoreApplyTag = view.getTag(R.id.qmui_skin_ignore_apply);\n        boolean ignoreApply = ignoreApplyTag instanceof Boolean && ((Boolean) ignoreApplyTag);\n        if (!ignoreApply) {\n            applyTheme(view, skinIndex, theme);\n        }\n        if (view instanceof ViewGroup) {\n            ViewGroup viewGroup = (ViewGroup) view;\n            if (sDispatchListenStrategySelector.select(viewGroup) == DispatchListenStrategy.LISTEN_ON_HIERARCHY_CHANGE) {\n                viewGroup.setOnHierarchyChangeListener(mOnHierarchyChangeListener);\n            } else {\n                viewGroup.addOnLayoutChangeListener(mOnLayoutChangeListener);\n            }\n            for (int i = 0; i < viewGroup.getChildCount(); i++) {\n                runDispatch(viewGroup.getChildAt(i), skinIndex, theme);\n            }\n        } else if (!ignoreApply && ((view instanceof TextView) || (view instanceof QMUIQQFaceView))) {\n            CharSequence text;\n            if (view instanceof TextView) {\n                text = ((TextView) view).getText();\n            } else {\n                text = ((QMUIQQFaceView) view).getText();\n            }\n            if (text instanceof Spanned) {\n                IQMUISkinHandlerSpan[] spans = ((Spanned) text).getSpans(0, text.length(), IQMUISkinHandlerSpan.class);\n                if (spans != null) {\n                    for (int i = 0; i < spans.length; i++) {\n                        spans[i].handle(view, this, skinIndex, theme);\n                    }\n                }\n                view.invalidate();\n            }\n        }\n    }\n\n    private void applyTheme(@NonNull View view, int skinIndex, Resources.Theme theme) {\n        SimpleArrayMap<String, Integer> attrs = getSkinAttrs(view);\n        try {\n            if (view instanceof IQMUISkinHandlerView) {\n                ((IQMUISkinHandlerView) view).handle(this, skinIndex, theme, attrs);\n            } else {\n                defaultHandleSkinAttrs(view, theme, attrs);\n            }\n\n            Object skinApplyListener = view.getTag(R.id.qmui_skin_apply_listener);\n            if (skinApplyListener instanceof IQMUISkinApplyListener) {\n                ((IQMUISkinApplyListener) skinApplyListener).onApply(view, skinIndex, theme);\n            }\n\n            if (view instanceof RecyclerView) {\n                RecyclerView recyclerView = (RecyclerView) view;\n                int itemDecorationCount = recyclerView.getItemDecorationCount();\n                for (int i = 0; i < itemDecorationCount; i++) {\n                    RecyclerView.ItemDecoration itemDecoration = recyclerView.getItemDecorationAt(i);\n                    if (itemDecoration instanceof IQMUISkinHandlerDecoration) {\n                        ((IQMUISkinHandlerDecoration) itemDecoration).handle(recyclerView, this, skinIndex, theme);\n                    }\n                }\n            }\n        } catch (Throwable throwable) {\n            QMUILog.printErrStackTrace(TAG, throwable,\n                    \"catch error when apply theme: \" + view.getClass().getSimpleName() +\n                            \"; \" + skinIndex + \"; attrs = \" + (attrs == null ? \"null\" : attrs.toString()));\n        }\n    }\n\n    void refreshRecyclerDecoration(@NonNull RecyclerView recyclerView,\n                                   @NonNull IQMUISkinHandlerDecoration decoration,\n                                   int skinIndex) {\n        SkinItem skinItem = mSkins.get(skinIndex);\n        if (skinItem != null) {\n            decoration.handle(recyclerView, this, skinIndex, skinItem.getTheme());\n        }\n    }\n\n    void refreshTheme(@NonNull View view, int skinIndex) {\n        SkinItem skinItem = mSkins.get(skinIndex);\n        if (skinItem != null) {\n            applyTheme(view, skinIndex, skinItem.getTheme());\n        }\n    }\n\n    public void defaultHandleSkinAttrs(@NonNull View view, Resources.Theme theme, @Nullable SimpleArrayMap<String, Integer> attrs) {\n        if (attrs != null) {\n            for (int i = 0; i < attrs.size(); i++) {\n                String key = attrs.keyAt(i);\n                Integer attr = attrs.valueAt(i);\n                if (attr == null) {\n                    continue;\n                }\n                defaultHandleSkinAttr(view, theme, key, attr);\n            }\n        }\n    }\n\n    public void defaultHandleSkinAttr(View view, Resources.Theme theme, String name, int attr) {\n        if (attr == 0) {\n            return;\n        }\n        IQMUISkinRuleHandler handler = sRuleHandlers.get(name);\n        if (handler == null) {\n            QMUILog.w(TAG, \"Do not find handler for skin attr name: \" + name);\n            return;\n        }\n        handler.handle(this, view, theme, name, attr);\n    }\n\n    @Nullable\n    private SimpleArrayMap<String, Integer> getSkinAttrs(View view) {\n        String skinValue = (String) view.getTag(R.id.qmui_skin_value);\n        String[] items;\n        if (skinValue == null || skinValue.isEmpty()) {\n            items = EMPTY_ITEMS;\n        } else {\n            items = skinValue.split(\"[|]\");\n        }\n\n        SimpleArrayMap<String, Integer> attrs = null;\n        if (view instanceof IQMUISkinDefaultAttrProvider) {\n            SimpleArrayMap<String, Integer> defaultAttrs = ((IQMUISkinDefaultAttrProvider) view).getDefaultSkinAttrs();\n            if (defaultAttrs != null && !defaultAttrs.isEmpty()) {\n                attrs = new SimpleArrayMap<>(defaultAttrs);\n            }\n        }\n        IQMUISkinDefaultAttrProvider provider = (IQMUISkinDefaultAttrProvider) view.getTag(\n                R.id.qmui_skin_default_attr_provider);\n        if (provider != null) {\n            SimpleArrayMap<String, Integer> providedAttrs = provider.getDefaultSkinAttrs();\n            if (providedAttrs != null && !providedAttrs.isEmpty()) {\n                if (attrs != null) {\n                    attrs.putAll(providedAttrs);\n                } else {\n                    attrs = new SimpleArrayMap<>(providedAttrs);\n                }\n            }\n        }\n\n        if (attrs == null) {\n            if (items.length <= 0) {\n                return null;\n            }\n            attrs = new SimpleArrayMap<>(items.length);\n        }\n\n        for (String item : items) {\n            String[] kv = item.split(\":\");\n            if (kv.length != 2) {\n                continue;\n            }\n            String key = kv[0].trim();\n            if (QMUILangHelper.isNullOrEmpty(key)) {\n                continue;\n            }\n            int attr = getAttrFromName(kv[1].trim());\n            if (attr == 0) {\n                QMUILog.w(TAG, \"Failed to get attr id from name: \" + kv[1]);\n                continue;\n            }\n            attrs.put(key, attr);\n        }\n        return attrs;\n    }\n\n    public int getAttrFromName(String attrName) {\n        return mResources.getIdentifier(attrName, \"attr\", mPackageName);\n    }\n\n    class SkinItem {\n        private int styleRes;\n\n        SkinItem(int styleRes) {\n            this.styleRes = styleRes;\n        }\n\n        public int getStyleRes() {\n            return styleRes;\n        }\n\n        @NonNull\n        Resources.Theme getTheme() {\n            Resources.Theme theme = sStyleIdThemeMap.get(styleRes);\n            if (theme == null) {\n                theme = mResources.newTheme();\n                theme.applyStyle(styleRes, true);\n                sStyleIdThemeMap.put(styleRes, theme);\n            }\n            return theme;\n        }\n    }\n\n    // =====================================================================================\n\n    private int mCurrentSkin = DEFAULT_SKIN;\n    private final List<WeakReference<?>> mSkinObserverList = new ArrayList<>();\n    private final List<OnSkinChangeListener> mSkinChangeListeners = new ArrayList<>();\n\n    public void register(@NonNull Activity activity) {\n        if (!containSkinObserver(activity)) {\n            mSkinObserverList.add(new WeakReference<>(activity));\n        }\n        dispatch(activity.findViewById(Window.ID_ANDROID_CONTENT), mCurrentSkin);\n    }\n\n    public void unRegister(@NonNull Activity activity) {\n        removeSkinObserver(activity);\n    }\n\n    public void register(@NonNull Fragment fragment) {\n        if (!containSkinObserver(fragment)) {\n            mSkinObserverList.add(new WeakReference<>(fragment));\n        }\n        dispatch(fragment.getView(), mCurrentSkin);\n    }\n\n    public void unRegister(@NonNull Fragment fragment) {\n        removeSkinObserver(fragment);\n    }\n\n    public void register(@NonNull View view) {\n        if (!containSkinObserver(view)) {\n            mSkinObserverList.add(new WeakReference<>(view));\n        }\n        dispatch(view, mCurrentSkin);\n    }\n\n    public void unRegister(@NonNull View view) {\n        removeSkinObserver(view);\n    }\n\n    public void register(@NonNull Dialog dialog) {\n        if (!containSkinObserver(dialog)) {\n            mSkinObserverList.add(new WeakReference<>(dialog));\n        }\n        Window window = dialog.getWindow();\n        if (window != null) {\n            dispatch(window.getDecorView(), mCurrentSkin);\n        }\n    }\n\n    public void unRegister(@NonNull Dialog dialog) {\n        removeSkinObserver(dialog);\n    }\n\n    public void register(@NonNull PopupWindow popupWindow) {\n        if (!containSkinObserver(popupWindow)) {\n            mSkinObserverList.add(new WeakReference<>(popupWindow));\n        }\n        dispatch(popupWindow.getContentView(), mCurrentSkin);\n    }\n\n    public void unRegister(@NonNull PopupWindow popupWindow) {\n        removeSkinObserver(popupWindow);\n    }\n\n    public void register(@NonNull Window window) {\n        if (!containSkinObserver(window)) {\n            mSkinObserverList.add(new WeakReference<>(window));\n        }\n        dispatch(window.getDecorView(), mCurrentSkin);\n    }\n\n    public void unRegister(@NonNull Window window) {\n        removeSkinObserver(window);\n    }\n\n    private void removeSkinObserver(Object object) {\n        for (int i = mSkinObserverList.size() - 1; i >= 0; i--) {\n            Object item = mSkinObserverList.get(i).get();\n            if (item == object) {\n                mSkinObserverList.remove(i);\n                return;\n            } else if (item == null) {\n                mSkinObserverList.remove(i);\n            }\n        }\n    }\n\n    private boolean containSkinObserver(Object object) {\n        //reverse order for remove\n        for (int i = mSkinObserverList.size() - 1; i >= 0; i--) {\n            Object item = mSkinObserverList.get(i).get();\n            if (item == object) {\n                return true;\n            } else if (item == null) {\n                mSkinObserverList.remove(i);\n            }\n        }\n        return false;\n    }\n\n    @MainThread\n    public void changeSkin(int index) {\n        if (mCurrentSkin == index) {\n            return;\n        }\n        int oldIndex = mCurrentSkin;\n        mCurrentSkin = index;\n        mIsInSkinChangeDispatch = true;\n        for (int i = mSkinObserverList.size() - 1; i >= 0; i--) {\n            Object item = mSkinObserverList.get(i).get();\n            if (item == null) {\n                mSkinObserverList.remove(i);\n            } else {\n                if (item instanceof Activity) {\n                    Activity activity = (Activity) item;\n                    activity.getWindow().setBackgroundDrawable(QMUIResHelper.getAttrDrawable(\n                            activity, mSkins.get(index).getTheme(), R.attr.qmui_skin_support_activity_background));\n                    dispatch(activity.findViewById(Window.ID_ANDROID_CONTENT), index);\n                } else if (item instanceof Fragment) {\n                    dispatch(((Fragment) item).getView(), index);\n                } else if (item instanceof Dialog) {\n                    Window window = ((Dialog) item).getWindow();\n                    if (window != null) {\n                        dispatch(window.getDecorView(), index);\n                    }\n                } else if (item instanceof PopupWindow) {\n                    dispatch(((PopupWindow) item).getContentView(), index);\n                } else if (item instanceof Window) {\n                    dispatch(((Window) item).getDecorView(), index);\n                } else if (item instanceof View) {\n                    dispatch((View) item, index);\n                }\n            }\n        }\n\n        for (int i = mSkinChangeListeners.size() - 1; i >= 0; i--) {\n            OnSkinChangeListener item = mSkinChangeListeners.get(i);\n            item.onSkinChange(this, oldIndex, mCurrentSkin);\n        }\n        mIsInSkinChangeDispatch = false;\n    }\n\n    @MainThread\n    public void addSkinChangeListener(@NonNull OnSkinChangeListener listener) {\n        if (mIsInSkinChangeDispatch) {\n            throw new RuntimeException(\"Can not add skinChangeListener while dispatching\");\n        }\n        mSkinChangeListeners.add(listener);\n    }\n\n    public void removeSkinChangeListener(@NonNull OnSkinChangeListener listener) {\n        if (mIsInSkinChangeDispatch) {\n            throw new RuntimeException(\"Can not add skinChangeListener while dispatching\");\n        }\n        mSkinChangeListeners.remove(listener);\n    }\n\n    public int getCurrentSkin() {\n        return mCurrentSkin;\n    }\n\n    public interface OnSkinChangeListener {\n        void onSkinChange(QMUISkinManager skinManager, int oldSkin, int newSkin);\n    }\n\n    class ViewSkinCurrent {\n        String managerName;\n        int index;\n\n        ViewSkinCurrent(String managerName, int index) {\n            this.managerName = managerName;\n            this.index = index;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            ViewSkinCurrent that = (ViewSkinCurrent) o;\n            return index == that.index &&\n                    Objects.equals(managerName, that.managerName);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(managerName, index);\n        }\n    }\n\n    public interface DispatchListenStrategySelector {\n        @NonNull\n        DispatchListenStrategy select(@NonNull ViewGroup viewGroup);\n    }\n\n    public enum DispatchListenStrategy {\n        LISTEN_ON_LAYOUT,\n        LISTEN_ON_HIERARCHY_CHANGE\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinValueBuilder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\n\nimport androidx.annotation.NonNull;\n\npublic class QMUISkinValueBuilder {\n    public static final String BACKGROUND = \"background\";\n    public static final String TEXT_COLOR = \"textColor\";\n    public static final String HINT_COLOR = \"hintColor\";\n    public static final String SECOND_TEXT_COLOR = \"secondTextColor\";\n    public static final String SRC = \"src\";\n    public static final String BORDER = \"border\";\n    public static final String TOP_SEPARATOR = \"topSeparator\";\n    public static final String BOTTOM_SEPARATOR = \"bottomSeparator\";\n    public static final String RIGHT_SEPARATOR = \"rightSeparator\";\n    public static final String LEFT_SEPARATOR = \"LeftSeparator\";\n    public static final String ALPHA = \"alpha\";\n    public static final String TINT_COLOR = \"tintColor\";\n    public static final String BG_TINT_COLOR = \"bgTintColor\";\n    public static final String PROGRESS_COLOR = \"progressColor\";\n    public static final String TEXT_COMPOUND_TINT_COLOR = \"tcTintColor\";\n    public static final String TEXT_COMPOUND_LEFT_SRC = \"tclSrc\";\n    public static final String TEXT_COMPOUND_RIGHT_SRC = \"tcrSrc\";\n    public static final String TEXT_COMPOUND_TOP_SRC = \"tctSrc\";\n    public static final String TEXT_COMPOUND_BOTTOM_SRC = \"tcbSrc\";\n    public static final String UNDERLINE = \"underline\";\n    public static final String MORE_TEXT_COLOR = \"moreTextColor\";\n    public static final String MORE_BG_COLOR = \"moreBgColor\";\n    private static LinkedList<QMUISkinValueBuilder> sValueBuilderPool;\n\n    public static QMUISkinValueBuilder acquire() {\n        if (sValueBuilderPool == null) {\n            return new QMUISkinValueBuilder();\n        }\n        QMUISkinValueBuilder valueBuilder = sValueBuilderPool.poll();\n        if (valueBuilder != null) {\n            return valueBuilder;\n        }\n        return new QMUISkinValueBuilder();\n    }\n\n    public static void release(@NonNull QMUISkinValueBuilder valueBuilder) {\n        valueBuilder.clear();\n        if (sValueBuilderPool == null) {\n            sValueBuilderPool = new LinkedList<>();\n        }\n        if (sValueBuilderPool.size() < 2) {\n            sValueBuilderPool.push(valueBuilder);\n        }\n    }\n\n    private QMUISkinValueBuilder() {\n\n    }\n\n    private HashMap<String, String> mValues = new HashMap<>();\n\n    public QMUISkinValueBuilder background(int attr) {\n        mValues.put(BACKGROUND, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder background(String attrName) {\n        mValues.put(BACKGROUND, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder underline(int attr) {\n        mValues.put(UNDERLINE, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder underline(String attrName) {\n        mValues.put(UNDERLINE, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder moreTextColor(int attr) {\n        mValues.put(MORE_TEXT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder moreTextColor(String attrName) {\n        mValues.put(MORE_TEXT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder moreBgColor(int attr) {\n        mValues.put(MORE_BG_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder moreBgColor(String attrName) {\n        mValues.put(MORE_BG_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundTintColor(int attr) {\n        mValues.put(TEXT_COMPOUND_TINT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundTintColor(String attrName) {\n        mValues.put(TEXT_COMPOUND_TINT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundTopSrc(int attr) {\n        mValues.put(TEXT_COMPOUND_TOP_SRC, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundTopSrc(String attrName) {\n        mValues.put(TEXT_COMPOUND_TOP_SRC, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundRightSrc(int attr) {\n        mValues.put(TEXT_COMPOUND_RIGHT_SRC, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundRightSrc(String attrName) {\n        mValues.put(TEXT_COMPOUND_RIGHT_SRC, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundBottomSrc(int attr) {\n        mValues.put(TEXT_COMPOUND_BOTTOM_SRC, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundBottomSrc(String attrName) {\n        mValues.put(TEXT_COMPOUND_BOTTOM_SRC, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundLeftSrc(int attr) {\n        mValues.put(TEXT_COMPOUND_LEFT_SRC, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textCompoundLeftSrc(String attrName) {\n        mValues.put(TEXT_COMPOUND_LEFT_SRC, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder textColor(int attr) {\n        mValues.put(TEXT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder textColor(String attrName) {\n        mValues.put(TEXT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder hintColor(int attr) {\n        mValues.put(HINT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder hintColor(String attrName) {\n        mValues.put(HINT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder progressColor(int attr) {\n        mValues.put(PROGRESS_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder progressColor(String attrName) {\n        mValues.put(PROGRESS_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder src(int attr) {\n        mValues.put(SRC, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder src(String attrName) {\n        mValues.put(SRC, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder border(int attr) {\n        mValues.put(BORDER, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder border(String attrName) {\n        mValues.put(BORDER, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder topSeparator(int attr) {\n        mValues.put(TOP_SEPARATOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder topSeparator(String attrName) {\n        mValues.put(TOP_SEPARATOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder rightSeparator(int attr) {\n        mValues.put(RIGHT_SEPARATOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder rightSeparator(String attrName) {\n        mValues.put(RIGHT_SEPARATOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder bottomSeparator(int attr) {\n        mValues.put(BOTTOM_SEPARATOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder bottomSeparator(String attrName) {\n        mValues.put(BOTTOM_SEPARATOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder leftSeparator(int attr) {\n        mValues.put(LEFT_SEPARATOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder leftSeparator(String attrName) {\n        mValues.put(LEFT_SEPARATOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder alpha(int attr) {\n        mValues.put(ALPHA, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder alpha(String attrName) {\n        mValues.put(ALPHA, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder tintColor(int attr) {\n        mValues.put(TINT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder tintColor(String attrName) {\n        mValues.put(TINT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder bgTintColor(int attr) {\n        mValues.put(BG_TINT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder bgTintColor(String attrName) {\n        mValues.put(BG_TINT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder secondTextColor(int attr) {\n        mValues.put(SECOND_TEXT_COLOR, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder secondTextColor(String attrName) {\n        mValues.put(SECOND_TEXT_COLOR, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder custom(String name, int attr) {\n        mValues.put(name, String.valueOf(attr));\n        return this;\n    }\n\n    public QMUISkinValueBuilder custom(String name, String attrName) {\n        mValues.put(name, attrName);\n        return this;\n    }\n\n    public QMUISkinValueBuilder clear() {\n        mValues.clear();\n        return this;\n    }\n\n    public QMUISkinValueBuilder convertFrom(String value) {\n        String[] items = value.split(\"[|]\");\n        for (String item : items) {\n            String[] kv = item.split(\":\");\n            if (kv.length != 2) {\n                continue;\n            }\n            mValues.put(kv[0].trim(), kv[1].trim());\n        }\n        return this;\n    }\n\n    public boolean isEmpty() {\n        return mValues.isEmpty();\n    }\n\n    public String build() {\n        StringBuilder builder = new StringBuilder();\n        boolean isFirstItem = true;\n        for (String name : mValues.keySet()) {\n            String itemValue = mValues.get(name);\n            if (itemValue == null || itemValue.isEmpty()) {\n                continue;\n            }\n            if (!isFirstItem) {\n                builder.append(\"|\");\n            }\n            builder.append(name);\n            builder.append(\":\");\n            builder.append(itemValue);\n            isFirstItem = false;\n        }\n        return builder.toString();\n    }\n\n    public void release() {\n        QMUISkinValueBuilder.release(this);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/SkinWriter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.skin;\n\npublic interface SkinWriter {\n    public void write(QMUISkinValueBuilder builder);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/annotation/QMUISkinChangeNotAdapted.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.annotation;\n\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface QMUISkinChangeNotAdapted {\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/annotation/QMUISkinListenWithHierarchyChange.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface QMUISkinListenWithHierarchyChange {\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/defaultAttr/IQMUISkinDefaultAttrProvider.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.defaultAttr;\n\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\npublic interface IQMUISkinDefaultAttrProvider {\n    @Nullable\n    SimpleArrayMap<String, Integer> getDefaultSkinAttrs();\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/defaultAttr/QMUISkinSimpleDefaultAttrProvider.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.defaultAttr;\n\nimport androidx.collection.SimpleArrayMap;\n\npublic class QMUISkinSimpleDefaultAttrProvider implements IQMUISkinDefaultAttrProvider {\n\n    private SimpleArrayMap<String, Integer> mSkinAttrs = new SimpleArrayMap<>();\n\n    public void setDefaultSkinAttr(String name, int attr) {\n        mSkinAttrs.put(name, attr);\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return mSkinAttrs;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/IQMUISkinRuleHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\n\npublic interface IQMUISkinRuleHandler {\n    void handle(@NonNull QMUISkinManager skinManager,\n                @NonNull View view,\n                @NonNull Resources.Theme theme,\n                @NonNull String name, int attr);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleAlphaHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.view.View;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleAlphaHandler extends QMUISkinRuleFloatHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, float value) {\n        view.setAlpha(value);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleBackgroundHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.Resources;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUIProgressBar;\nimport com.qmuiteam.qmui.widget.QMUISlider;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleBackgroundHandler implements IQMUISkinRuleHandler {\n\n    @Override\n    public void handle(@NotNull QMUISkinManager skinManager, @NotNull View view, @NotNull Resources.Theme theme, @NotNull String name, int attr) {\n        if(view instanceof QMUIRoundButton){\n            ((QMUIRoundButton)view).setBgData(\n                    QMUIResHelper.getAttrColorStateList(view.getContext(), theme, attr));\n        }else if(view instanceof QMUIProgressBar){\n            view.setBackgroundColor(QMUIResHelper.getAttrColor(theme, attr));\n        }else if(view instanceof QMUISlider){\n            ((QMUISlider)view).setBarNormalColor(QMUIResHelper.getAttrColor(theme, attr));\n        }else{\n            QMUIViewHelper.setBackgroundKeepingPadding(view,\n                    QMUIResHelper.getAttrDrawable(view.getContext(), theme, attr));\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleBgTintColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\n\nimport androidx.core.view.TintableBackgroundView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleBgTintColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if (view instanceof TintableBackgroundView) {\n            ((TintableBackgroundView) view).setSupportBackgroundTintList(colorStateList);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleBorderHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.layout.IQMUILayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView;\nimport com.qmuiteam.qmui.widget.QMUISlider;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleBorderHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if(colorStateList == null){\n            return;\n        }\n        if (view instanceof IQMUILayout) {\n            ((IQMUILayout) view).setBorderColor(colorStateList.getDefaultColor());\n        } else if (view instanceof QMUIRadiusImageView) {\n            ((QMUIRadiusImageView) view).setBorderColor(colorStateList.getDefaultColor());\n        } else if (view instanceof QMUIRoundButton) {\n            ((QMUIRoundButton) view).setStrokeColors(colorStateList);\n        } else if(view instanceof QMUISlider.DefaultThumbView){\n            ((QMUISlider.DefaultThumbView)view).setBorderColor(colorStateList.getDefaultColor());\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic abstract class QMUISkinRuleColorHandler implements IQMUISkinRuleHandler {\n    @Override\n    public final void handle(@NotNull QMUISkinManager skinManager, @NotNull View view, @NotNull Resources.Theme theme,\n                             @NotNull String name, int attr) {\n        handle(view, name, QMUIResHelper.getAttrColor(theme, attr));\n    }\n\n    protected abstract void handle(@NonNull View view, @NonNull String name, int color);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleColorStateListHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic abstract class QMUISkinRuleColorStateListHandler implements IQMUISkinRuleHandler {\n    @Override\n    public final void handle(@NotNull QMUISkinManager skinManager, @NotNull View view, @NotNull Resources.Theme theme,\n                             @NotNull String name, int attr) {\n        handle(view, name, QMUIResHelper.getAttrColorStateList(view.getContext(), theme, attr));\n    }\n\n    protected abstract void handle(@NonNull View view,\n                                   @NonNull String name,\n                                   ColorStateList colorStateList);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleDrawableHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.Resources;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic abstract class QMUISkinRuleDrawableHandler implements IQMUISkinRuleHandler {\n    @Override\n    public final void handle(@NotNull @NonNull QMUISkinManager skinManager,\n                             @NotNull @NonNull View view,\n                             @NotNull @NonNull Resources.Theme theme,\n                             @NotNull @NonNull String name, int attr) {\n        handle(view, name, QMUIResHelper.getAttrDrawable(view.getContext(), theme, attr));\n    }\n\n    protected abstract void handle(@NonNull View view, @NonNull String name, Drawable drawable);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleFloatHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic abstract class QMUISkinRuleFloatHandler implements IQMUISkinRuleHandler {\n    @Override\n    public final void handle(@NotNull QMUISkinManager skinManager, @NotNull View view, @NotNull Resources.Theme theme,\n                             @NotNull String name, int attr) {\n        handle(view, name, QMUIResHelper.getAttrFloatValue(theme, attr));\n    }\n\n    protected abstract void handle(@NonNull View view,\n                                   @NonNull String name, float value);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleHintColorHandler.java",
    "content": "package com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.google.android.material.textfield.TextInputLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.widget.QMUISlider;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleHintColorHandler extends QMUISkinRuleColorStateListHandler {\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if (view instanceof TextView) {\n            ((TextView) view).setHintTextColor(colorStateList);\n        } else if (view instanceof TextInputLayout) {\n            ((TextInputLayout) view).setHintTextColor(colorStateList);\n        }else if(view instanceof QMUISlider){\n            ((QMUISlider)view).setRecordProgressColor(colorStateList.getDefaultColor());\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleMoreBgColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleMoreBgColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if (view instanceof QMUIQQFaceView) {\n            ((QMUIQQFaceView) view).setMoreActionBgColor(colorStateList);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleMoreTextColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleMoreTextColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if (view instanceof QMUIQQFaceView) {\n            ((QMUIQQFaceView) view).setMoreActionColor(colorStateList);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleProgressColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.widget.QMUIProgressBar;\nimport com.qmuiteam.qmui.widget.QMUISlider;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleProgressColorHandler extends QMUISkinRuleColorHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, int color) {\n        if (view instanceof QMUIProgressBar) {\n            ((QMUIProgressBar) view).setProgressColor(color);\n        }else if(view instanceof QMUISlider){\n            ((QMUISlider) view).setBarProgressColor(color);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleSeparatorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.view.View;\n\nimport com.qmuiteam.qmui.layout.IQMUILayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleSeparatorHandler extends QMUISkinRuleColorHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, int color) {\n        if (view instanceof IQMUILayout) {\n            if (QMUISkinValueBuilder.TOP_SEPARATOR.equals(name)) {\n                ((IQMUILayout) view).updateTopSeparatorColor(color);\n            } else if (QMUISkinValueBuilder.BOTTOM_SEPARATOR.equals(name)) {\n                ((IQMUILayout) view).updateBottomSeparatorColor(color);\n            } else if (QMUISkinValueBuilder.LEFT_SEPARATOR.equals(name)) {\n                ((IQMUILayout) view).updateLeftSeparatorColor(color);\n            } else if (QMUISkinValueBuilder.RIGHT_SEPARATOR.equals(name)) {\n                ((IQMUILayout) view).updateRightSeparatorColor(color);\n            }\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleSrcHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\nimport android.widget.CompoundButton;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleSrcHandler extends QMUISkinRuleDrawableHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, Drawable drawable) {\n\n        if (view instanceof ImageView) {\n            ((ImageView) view).setImageDrawable(drawable);\n        } else if (view instanceof CompoundButton) {\n            ((CompoundButton) view).setButtonDrawable(drawable);\n        } else {\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleTextColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.widget.QMUIProgressBar;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleTextColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if(colorStateList == null){\n            return;\n        }\n        if (view instanceof TextView) {\n            ((TextView) view).setTextColor(colorStateList);\n        } else if (view instanceof QMUIQQFaceView) {\n            ((QMUIQQFaceView) view).setTextColor(colorStateList.getDefaultColor());\n        }else if(view instanceof QMUIProgressBar){\n            ((QMUIProgressBar) view).setTextColor(colorStateList.getDefaultColor());\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleTextCompoundSrcHandler.java",
    "content": "package com.qmuiteam.qmui.skin.handler;\n\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleTextCompoundSrcHandler extends QMUISkinRuleDrawableHandler {\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, Drawable drawable) {\n        if (view instanceof TextView) {\n            TextView tv = (TextView) view;\n            if (drawable != null) {\n                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n            }\n            Drawable[] drawables = tv.getCompoundDrawables();\n            if (QMUISkinValueBuilder.TEXT_COMPOUND_LEFT_SRC.equals(name)) {\n                drawables[0] = drawable;\n            } else if (QMUISkinValueBuilder.TEXT_COMPOUND_TOP_SRC.equals(name)) {\n                drawables[1] = drawable;\n            } else if (QMUISkinValueBuilder.TEXT_COMPOUND_RIGHT_SRC.equals(name)) {\n                drawables[2] = drawable;\n            } else if (QMUISkinValueBuilder.TEXT_COMPOUND_BOTTOM_SRC.equals(name)) {\n                drawables[3] = drawable;\n            }\n            tv.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleTextCompoundTintColorHandler.java",
    "content": "package com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.util.QMUIDrawableHelper;\n\nimport androidx.core.widget.TintableCompoundDrawablesView;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleTextCompoundTintColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if(colorStateList == null){\n            return;\n        }\n        if (view instanceof TextView) {\n            TextView tv = (TextView) view;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                tv.setCompoundDrawableTintList(colorStateList);\n            } else if (tv instanceof TintableCompoundDrawablesView) {\n                ((TintableCompoundDrawablesView) tv).setSupportCompoundDrawablesTintList(colorStateList);\n            } else {\n                Drawable[] drawables = tv.getCompoundDrawables();\n                for (int i = 0; i < drawables.length; i++) {\n                    Drawable drawable = drawables[i];\n                    if (drawable != null) {\n                        drawable = drawable.mutate();\n                        QMUIDrawableHelper.setDrawableTintColor(drawable, colorStateList.getDefaultColor());\n                        drawables[i] = drawable;\n                    }\n                }\n                tv.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);\n            }\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleTintColorHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\nimport android.widget.CompoundButton;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUILoadingView;\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout;\n\nimport androidx.core.widget.CompoundButtonCompat;\nimport androidx.core.widget.ImageViewCompat;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleTintColorHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if(colorStateList == null){\n            return;\n        }\n        if(view instanceof QMUILoadingView){\n            ((QMUILoadingView) view).setColor(colorStateList.getDefaultColor());\n        }else if(view instanceof QMUIPullRefreshLayout.RefreshView){\n            ((QMUIPullRefreshLayout.RefreshView)view).setColorSchemeColors(colorStateList.getDefaultColor());\n        }else if (view instanceof ImageView) {\n            ImageViewCompat.setImageTintList((ImageView) view, colorStateList);\n        }else if(view instanceof CompoundButton){\n            CompoundButtonCompat.setButtonTintList((CompoundButton)view, colorStateList);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/skin/handler/QMUISkinRuleUnderlineHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.skin.handler;\n\nimport android.content.res.ColorStateList;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.widget.QMUIProgressBar;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUISkinRuleUnderlineHandler extends QMUISkinRuleColorStateListHandler {\n\n    @Override\n    protected void handle(@NotNull View view, @NotNull String name, ColorStateList colorStateList) {\n        if (view instanceof QMUIQQFaceView) {\n            ((QMUIQQFaceView) view).setLinkUnderLineColor(colorStateList);\n        }else{\n            QMUISkinHelper.warnRuleNotSupport(view, name);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUIAlignMiddleImageSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.text.style.ImageSpan;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerSpan;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIDrawableHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport androidx.annotation.NonNull;\n\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 支持垂直居中的ImageSpan\n *\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUIAlignMiddleImageSpan extends ImageSpan implements IQMUISkinHandlerSpan {\n\n    public static final int ALIGN_MIDDLE = -100; // 不要和父类重复\n\n    /**\n     * 规定这个Span占几个字的宽度\n     */\n    private float mFontWidthMultiple = -1f;\n\n    /**\n     * 是否避免父类修改FontMetrics，如果为 false 则会走父类的逻辑, 会导致FontMetrics被更改\n     */\n    private boolean mAvoidSuperChangeFontMetrics = false;\n\n    @SuppressWarnings(\"FieldCanBeLocal\") private int mWidth;\n    private Drawable mDrawable;\n    private int mDrawableTintColorAttr;\n\n    /**\n     * @param d                 作为 span 的 Drawable\n     * @param verticalAlignment 垂直对齐方式, 如果要垂直居中, 则使用 {@link #ALIGN_MIDDLE}\n     */\n    public QMUIAlignMiddleImageSpan(Drawable d, int verticalAlignment) {\n        this(d, verticalAlignment, 0);\n    }\n\n    /**\n     * @param d                 作为 span 的 Drawable\n     * @param verticalAlignment 垂直对齐方式, 如果要垂直居中, 则使用 {@link #ALIGN_MIDDLE}\n     * @param fontWidthMultiple 设置这个Span占几个中文字的宽度, 当该值 > 0 时, span 的宽度为该值*一个中文字的宽度; 当该值 <= 0 时, span 的宽度由 {@link #mAvoidSuperChangeFontMetrics} 决定\n     */\n    public QMUIAlignMiddleImageSpan(@NonNull Drawable d, int verticalAlignment, float fontWidthMultiple) {\n        super(d.mutate(), verticalAlignment);\n        mDrawable = getDrawable();\n        if (fontWidthMultiple >= 0) {\n            mFontWidthMultiple = fontWidthMultiple;\n        }\n    }\n\n    public void setSkinSupportWithTintColor(View skinFollowView, int drawableTintColorAttr) {\n        mDrawableTintColorAttr = drawableTintColorAttr;\n        if (mDrawable != null && skinFollowView != null && drawableTintColorAttr != 0) {\n            QMUIDrawableHelper.setDrawableTintColor(mDrawable,\n                    QMUISkinHelper.getSkinColor(skinFollowView, drawableTintColorAttr));\n            skinFollowView.invalidate();\n        }\n    }\n\n    @Override\n    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {\n        if (mAvoidSuperChangeFontMetrics) {\n            Drawable d = getDrawable();\n            Rect rect = d.getBounds();\n            mWidth = rect.right;\n        } else {\n            mWidth = super.getSize(paint, text, start, end, fm);\n        }\n        if (mFontWidthMultiple > 0) {\n            mWidth = (int) (paint.measureText(\"子\") * mFontWidthMultiple);\n        }\n        return mWidth;\n    }\n\n    @Override\n    public void draw(Canvas canvas, CharSequence text, int start, int end,\n                     float x, int top, int y, int bottom, Paint paint) {\n        if (mVerticalAlignment == ALIGN_MIDDLE) {\n            Drawable d = mDrawable;\n            canvas.save();\n\n//            // 注意如果这样实现会有问题：TextView 有 lineSpacing 时，这里 bottom 偏大，导致偏下\n//            int transY = bottom - d.getBounds().bottom; // 底对齐\n//            transY -= (paint.getFontMetricsInt().bottom - paint.getFontMetricsInt().top) / 2 - d.getBounds().bottom / 2; // 居中对齐\n//            canvas.translate(x, transY);\n//            d.draw(canvas);\n//            canvas.restore();\n\n            Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();\n            int fontTop = y + fontMetricsInt.top;\n            int fontMetricsHeight = fontMetricsInt.bottom - fontMetricsInt.top;\n            int iconHeight = d.getBounds().bottom - d.getBounds().top;\n            int iconTop = fontTop + (fontMetricsHeight - iconHeight) / 2;\n            canvas.translate(x, iconTop);\n            d.draw(canvas);\n            canvas.restore();\n        } else {\n            super.draw(canvas, text, start, end, x, top, y, bottom, paint);\n        }\n    }\n\n    /**\n     * 是否避免父类修改FontMetrics，如果为 false 则会走父类的逻辑, 会导致FontMetrics被更改\n     */\n    public void setAvoidSuperChangeFontMetrics(boolean avoidSuperChangeFontMetrics) {\n        mAvoidSuperChangeFontMetrics = avoidSuperChangeFontMetrics;\n    }\n\n    @Override\n    public void handle(@NotNull View view, @NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme) {\n        if (mDrawableTintColorAttr != 0) {\n            QMUIDrawableHelper.setDrawableTintColor(mDrawable,\n                    QMUIResHelper.getAttrColor(theme, mDrawableTintColorAttr));\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUIBlockSpaceSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport androidx.annotation.NonNull;\nimport android.text.style.ReplacementSpan;\n\nimport com.qmuiteam.qmui.util.QMUIDeviceHelper;\n\n/**\n * 提供一个整行的空白的Span，可用来用于制作段间距\n *\n * @author cginechen\n * @date 2016-02-17\n */\npublic class QMUIBlockSpaceSpan extends ReplacementSpan {\n    private int mHeight;\n\n    public QMUIBlockSpaceSpan(int height) {\n        mHeight = height;\n    }\n\n    @Override\n    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {\n        if (fm != null && !QMUIDeviceHelper.isMeizu()) {\n            //return后宽度为0，因此实际空隙和段落开始在同一行，需要加上一行的高度\n            fm.ascent = fm.top = -mHeight - paint.getFontMetricsInt(fm);\n            fm.descent = fm.bottom = 0;\n        }\n        return 0;\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {\n\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUICustomTypefaceSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport androidx.annotation.Nullable;\nimport android.text.TextPaint;\nimport android.text.style.TypefaceSpan;\n\n/**\n * 支持以 Typeface 的方式设置 span 的字体，实现自定义字体的效果\n */\npublic class QMUICustomTypefaceSpan extends TypefaceSpan {\n\n    /* http://stackoverflow.com/questions/6612316/how-set-spannable-object-font-with-custom-font#answer-10741161 */\n\n    public static final Parcelable.Creator<QMUICustomTypefaceSpan> CREATOR = new Parcelable.Creator<QMUICustomTypefaceSpan>() {\n        @Override\n        public QMUICustomTypefaceSpan createFromParcel(Parcel source) {\n            return null;\n        }\n\n        @Override\n        public QMUICustomTypefaceSpan[] newArray(int size) {\n            return new QMUICustomTypefaceSpan[size];\n        }\n    };\n    private final @Nullable Typeface newType;\n\n    /**\n     * @param family Typeface 字体的字体名\n     * @param type 该字体的 Typeface 对象\n     */\n    public QMUICustomTypefaceSpan(String family, @Nullable Typeface type) {\n        super(family);\n        newType = type;\n    }\n\n    private static void applyCustomTypeFace(Paint paint, @Nullable Typeface tf) {\n        if (tf == null) {\n            return;\n        }\n\n        int oldStyle;\n        Typeface old = paint.getTypeface();\n        if (old == null) {\n            oldStyle = Typeface.NORMAL;\n        } else {\n            oldStyle = old.getStyle();\n        }\n\n        int fake = oldStyle & ~tf.getStyle();\n        if ((fake & Typeface.BOLD) != 0) {\n            paint.setFakeBoldText(true);\n        }\n\n        if ((fake & Typeface.ITALIC) != 0) {\n            paint.setTextSkewX(-0.25f);\n        }\n\n        paint.setTypeface(tf);\n    }\n\n    @Override\n    public void updateDrawState(TextPaint ds) {\n        applyCustomTypeFace(ds, newType);\n    }\n\n    @Override\n    public void updateMeasureState(TextPaint paint) {\n        applyCustomTypeFace(paint, newType);\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUIMarginImageSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\n\n/**\n * 支持设置图片左右间距的 ImageSpan\n *\n * @author chantchen\n * @date 2015-12-16\n */\npublic class QMUIMarginImageSpan extends QMUIAlignMiddleImageSpan {\n\n    private int mSpanMarginLeft = 0;\n    private int mSpanMarginRight = 0;\n    private int mOffsetY = 0;\n\n    public QMUIMarginImageSpan(Drawable d, int verticalAlignment, int marginLeft, int marginRight) {\n        this(d, verticalAlignment, marginLeft, marginRight, 0);\n    }\n\n    public QMUIMarginImageSpan(Drawable d, int verticalAlignment, int marginLeft, int marginRight, int offsetY) {\n        super(d, verticalAlignment);\n        mSpanMarginLeft = marginLeft;\n        mSpanMarginRight = marginRight;\n        mOffsetY = offsetY;\n    }\n\n    @Override\n    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {\n        if (mSpanMarginLeft != 0 || mSpanMarginRight != 0) {\n            super.getSize(paint, text, start, end, fm);\n            Drawable d = getDrawable();\n            return d.getIntrinsicWidth() + mSpanMarginLeft + mSpanMarginRight;\n        } else {\n            return super.getSize(paint, text, start, end, fm);\n        }\n    }\n\n    @Override\n    public void draw(Canvas canvas, CharSequence text, int start, int end,\n                     float x, int top, int y, int bottom, Paint paint) {\n        canvas.save();\n        canvas.translate(0, mOffsetY);\n        // marginRight不用专门处理，只靠getSize()中改变即可\n        super.draw(canvas, text, start, end, x + mSpanMarginLeft, top, y, bottom, paint);\n        canvas.restore();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUIOnSpanClickListener.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\n/**\n * @author cginechen\n * @date 2017-03-20\n */\n\npublic interface QMUIOnSpanClickListener {\n    boolean onSpanClick(String text);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUITextSizeSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.text.style.ReplacementSpan;\n\nimport androidx.annotation.NonNull;\n\n/**\n * 支持调整字体大小的 span。{@link android.text.style.AbsoluteSizeSpan} 可以调整字体大小，但在中英文混排下由于 decent 的不同，\n * 无法根据具体需求进行底部对齐或者顶部对齐。而 QMUITextSizeSpan 则可以多传一个参数，让你可以根据具体情况来决定偏移值。\n *\n * @author cginechen\n * @date 2016-12-02\n */\n\npublic class QMUITextSizeSpan extends ReplacementSpan {\n    private int mTextSize;\n    private int mVerticalOffset;\n    private Paint mPaint;\n    private Typeface mTypeface;\n\n    public QMUITextSizeSpan(int textSize, int verticalOffset){\n       this(textSize, verticalOffset, null);\n    }\n\n    public QMUITextSizeSpan(int textSize, int verticalOffset, Typeface typeface){\n        mTextSize = textSize;\n        mVerticalOffset = verticalOffset;\n        mTypeface = typeface;\n        mPaint = new Paint();\n        mPaint.setTextSize(mTextSize);\n        mPaint.setTypeface(mTypeface);\n    }\n\n    @Override\n    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {\n        if(mTextSize > paint.getTextSize() && fm != null){\n            Paint.FontMetricsInt newFm = mPaint.getFontMetricsInt();\n            fm.descent = newFm.descent;\n            fm.ascent = newFm.ascent;\n            fm.top = newFm.top;\n            fm.bottom = newFm.bottom;\n        }\n        return (int) mPaint.measureText(text, start, end);\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top,\n                     int y, int bottom, @NonNull Paint paint) {\n        mPaint.setColor(paint.getColor());\n        mPaint.setStyle(paint.getStyle());\n        mPaint.setAntiAlias(paint.isAntiAlias());\n        int baseline = y + mVerticalOffset;\n        canvas.drawText(text, start, end, x, baseline, mPaint);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/span/QMUITouchableSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.span;\n\nimport android.content.res.Resources;\nimport android.text.TextPaint;\nimport android.text.style.ClickableSpan;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.QMUILog;\nimport com.qmuiteam.qmui.link.ITouchableSpan;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerSpan;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport androidx.annotation.ColorInt;\nimport androidx.core.view.ViewCompat;\n\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 可 Touch 的 Span，在 {@link #setPressed(boolean)} 后根据是否 pressed 来触发不同的UI状态\n * <p>\n * 提供设置 span 的文字颜色和背景颜色的功能, 在构造时传入\n * </p>\n */\npublic abstract class QMUITouchableSpan extends ClickableSpan implements ITouchableSpan, IQMUISkinHandlerSpan {\n    private static final String TAG = \"QMUITouchableSpan\";\n    private boolean mIsPressed;\n    @ColorInt private int mNormalBackgroundColor;\n    @ColorInt private int mPressedBackgroundColor;\n    @ColorInt private int mNormalTextColor;\n    @ColorInt private int mPressedTextColor;\n\n    private int mNormalBgAttr;\n    private int mPressedBgAttr;\n    private int mNormalTextColorAttr;\n    private int mPressedTextColorAttr;\n\n    private boolean mIsNeedUnderline = false;\n\n    public abstract void onSpanClick(View widget);\n\n    @Override\n    public final void onClick(View widget) {\n        if (ViewCompat.isAttachedToWindow(widget)) {\n            onSpanClick(widget);\n        }\n    }\n\n\n    public QMUITouchableSpan(@ColorInt int normalTextColor,\n                             @ColorInt int pressedTextColor,\n                             @ColorInt int normalBackgroundColor,\n                             @ColorInt int pressedBackgroundColor) {\n        mNormalTextColor = normalTextColor;\n        mPressedTextColor = pressedTextColor;\n        mNormalBackgroundColor = normalBackgroundColor;\n        mPressedBackgroundColor = pressedBackgroundColor;\n    }\n\n    public QMUITouchableSpan(View initFollowSkinView,\n                             int normalTextColorAttr, int pressedTextColorAttr,\n                             int normalBgAttr, int pressedBgAttr) {\n        mNormalBgAttr = normalBgAttr;\n        mPressedBgAttr = pressedBgAttr;\n        mNormalTextColorAttr = normalTextColorAttr;\n        mPressedTextColorAttr = pressedTextColorAttr;\n        if (normalTextColorAttr != 0) {\n            mNormalTextColor = QMUISkinHelper.getSkinColor(initFollowSkinView, normalTextColorAttr);\n        }\n        if (pressedTextColorAttr != 0) {\n            mPressedTextColor = QMUISkinHelper.getSkinColor(initFollowSkinView, pressedTextColorAttr);\n        }\n        if (normalBgAttr != 0) {\n            mNormalBackgroundColor = QMUISkinHelper.getSkinColor(initFollowSkinView, normalBgAttr);\n        }\n        if (pressedBgAttr != 0) {\n            mPressedBackgroundColor = QMUISkinHelper.getSkinColor(initFollowSkinView, pressedBgAttr);\n        }\n    }\n\n    public int getNormalBackgroundColor() {\n        return mNormalBackgroundColor;\n    }\n\n    public void setNormalTextColor(int normalTextColor) {\n        mNormalTextColor = normalTextColor;\n    }\n\n    public void setPressedTextColor(int pressedTextColor) {\n        mPressedTextColor = pressedTextColor;\n    }\n\n    public int getNormalTextColor() {\n        return mNormalTextColor;\n    }\n\n    public int getPressedBackgroundColor() {\n        return mPressedBackgroundColor;\n    }\n\n    public int getPressedTextColor() {\n        return mPressedTextColor;\n    }\n\n    public void setPressed(boolean isSelected) {\n        mIsPressed = isSelected;\n    }\n\n    public boolean isPressed() {\n        return mIsPressed;\n    }\n\n    public void setIsNeedUnderline(boolean isNeedUnderline) {\n        mIsNeedUnderline = isNeedUnderline;\n    }\n\n    public boolean isNeedUnderline() {\n        return mIsNeedUnderline;\n    }\n\n    @Override\n    public void updateDrawState(TextPaint ds) {\n        ds.setColor(mIsPressed ? mPressedTextColor : mNormalTextColor);\n        ds.bgColor = mIsPressed ? mPressedBackgroundColor\n                : mNormalBackgroundColor;\n        ds.setUnderlineText(mIsNeedUnderline);\n    }\n\n    @Override\n    public void handle(@NotNull View view, @NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme) {\n        boolean noAttrExist = true;\n        if (mNormalTextColorAttr != 0) {\n            mNormalTextColor = QMUIResHelper.getAttrColor(theme, mNormalTextColorAttr);\n            noAttrExist = false;\n        }\n        if (mPressedTextColorAttr != 0) {\n            mPressedTextColor = QMUIResHelper.getAttrColor(theme, mPressedTextColorAttr);\n            noAttrExist = false;\n        }\n        if (mNormalBgAttr != 0) {\n            mNormalBackgroundColor = QMUIResHelper.getAttrColor(theme, mNormalBgAttr);\n            noAttrExist = false;\n        }\n        if (mPressedBgAttr != 0) {\n            mPressedBackgroundColor = QMUIResHelper.getAttrColor(theme, mPressedBgAttr);\n            noAttrExist = false;\n        }\n\n        if (noAttrExist) {\n            QMUILog.w(TAG, \"There are no attrs for skin. Please use constructor with 5 parameters\");\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/OnceReadValue.java",
    "content": "package com.qmuiteam.qmui.util;\n\npublic abstract class OnceReadValue<P, T> {\n\n    private volatile boolean isRead = false;\n    private T cacheValue;\n\n    public T get(P param){\n        if(isRead){\n            return cacheValue;\n        }\n        synchronized (this){\n            if(!isRead){\n                cacheValue = read(param);\n                isRead = true;\n            }\n        }\n        return cacheValue;\n    }\n\n    protected abstract T read(P param);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIActivityLifecycleCallbacks.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Bundle;\n\n/**\n * @author cginechen\n * @date 2016-11-07\n *\n * https://github.com/yshrsmz/KeyboardVisibilityEvent/blob/master/keyboardvisibilityevent/src/main/java/net/yslibrary/android/keyboardvisibilityevent/AutoActivityLifecycleCallback.java\n */\n\npublic abstract class QMUIActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {\n\n    private final Activity mTargetActivity;\n\n    public QMUIActivityLifecycleCallbacks(Activity targetActivity) {\n        mTargetActivity = targetActivity;\n    }\n\n    @Override\n    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {\n\n    }\n\n    @Override\n    public void onActivityStarted(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityResumed(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityPaused(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityStopped(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {\n\n    }\n\n    @Override\n    public void onActivityDestroyed(Activity activity) {\n        if (activity == mTargetActivity) {\n            mTargetActivity.getApplication().unregisterActivityLifecycleCallbacks(this);\n            onTargetActivityDestroyed();\n        }\n    }\n\n    protected abstract void onTargetActivityDestroyed();\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUICollapsingTextHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2015 The Android Open Source Project\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.qmuiteam.qmui.util;\n\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.Typeface;\nimport android.os.Build;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.animation.Interpolator;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.RequiresApi;\nimport androidx.core.text.TextDirectionHeuristicsCompat;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.R;\n\npublic final class QMUICollapsingTextHelper {\n\n    // Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it\n    // by using our own texture\n    private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;\n\n    private static final boolean DEBUG_DRAW = false;\n    private static final Paint DEBUG_DRAW_PAINT;\n\n    static {\n        // 测试逻辑，不作检测\n        // noinspection ConstantConditions\n        DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;\n        // noinspection ConstantConditions\n        if (DEBUG_DRAW_PAINT != null) {\n            DEBUG_DRAW_PAINT.setAntiAlias(true);\n            DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);\n        }\n    }\n\n    private final View mView;\n\n    private boolean mDrawTitle;\n    private float mExpandedFraction;\n\n    private final Rect mExpandedBounds;\n    private final Rect mCollapsedBounds;\n    private final RectF mCurrentBounds;\n    private int mExpandedTextGravity = Gravity.CENTER_VERTICAL;\n    private int mCollapsedTextGravity = Gravity.CENTER_VERTICAL;\n    private float mExpandedTextSize = 15;\n    private float mCollapsedTextSize = 15;\n    private ColorStateList mExpandedTextColor;\n    private ColorStateList mCollapsedTextColor;\n\n    private float mExpandedDrawY;\n    private float mCollapsedDrawY;\n    private float mExpandedDrawX;\n    private float mCollapsedDrawX;\n    private float mCurrentDrawX;\n    private float mCurrentDrawY;\n    private float mCollapsedTextWidth;\n    private float mExpandedTextWidth;\n    private float mCurrentTextWidth;\n    private float mCollapsedTextHeight;\n    private float mExpandedTextHeight;\n    private float mCurrentTextHeight;\n    private Typeface mCollapsedTypeface;\n    private Typeface mExpandedTypeface;\n    private Typeface mCurrentTypeface;\n    private float mTypefaceUpdateAreaPercent;\n\n    private CharSequence mText;\n    private CharSequence mTextToDraw;\n    private boolean mIsRtl;\n\n    private boolean mUseTexture;\n    private Bitmap mExpandedTitleTexture;\n    private Paint mTexturePaint;\n    private float mTextureAscent;\n    private float mTextureDescent;\n\n    private float mScale;\n    private float mCurrentTextSize;\n\n    private int[] mState;\n\n    private boolean mBoundsChanged;\n\n    private final TextPaint mTextPaint;\n\n    private Interpolator mPositionInterpolator;\n    private Interpolator mTextSizeInterpolator;\n\n    private float mCollapsedShadowRadius, mCollapsedShadowDx, mCollapsedShadowDy;\n    private int mCollapsedShadowColor;\n\n    private float mExpandedShadowRadius, mExpandedShadowDx, mExpandedShadowDy;\n    private int mExpandedShadowColor;\n\n    public QMUICollapsingTextHelper(View view){\n        this(view, 0f);\n    }\n\n    public QMUICollapsingTextHelper(View view, float defaultExpanededFraction) {\n        mView = view;\n\n        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);\n\n        mExpandedFraction = defaultExpanededFraction;\n        mCollapsedBounds = new Rect();\n        mExpandedBounds = new Rect();\n        mCurrentBounds = new RectF();\n    }\n\n    public void setTextSizeInterpolator(Interpolator interpolator) {\n        mTextSizeInterpolator = interpolator;\n        recalculate();\n    }\n\n    public void setPositionInterpolator(Interpolator interpolator) {\n        mPositionInterpolator = interpolator;\n        recalculate();\n    }\n\n    public void setTextSize(float collapsedTextSize, float expandedTextSize, boolean recalculate){\n        if(mExpandedTextSize != expandedTextSize || mCollapsedTextSize != collapsedTextSize){\n            mExpandedTextSize = expandedTextSize;\n            mCollapsedTextSize = collapsedTextSize;\n            if(recalculate){\n                recalculate();\n            }\n        }\n    }\n\n    public void setExpandedTextSize(float textSize) {\n        if (mExpandedTextSize != textSize) {\n            mExpandedTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedTextSize(float textSize) {\n        if (mCollapsedTextSize != textSize) {\n            mCollapsedTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedTextColor(ColorStateList textColor) {\n        if (mCollapsedTextColor != textColor) {\n            mCollapsedTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setExpandedTextColor(ColorStateList textColor) {\n        if (mExpandedTextColor != textColor) {\n            mExpandedTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setTextColor(ColorStateList collapsedTextColor, ColorStateList expandedTextColor,\n                             boolean recalculate){\n        if(mCollapsedTextColor != collapsedTextColor || mExpandedTextColor != expandedTextColor){\n            mCollapsedTextColor = collapsedTextColor;\n            mExpandedTextColor = expandedTextColor;\n            if(recalculate){\n                recalculate();\n            }\n        }\n    }\n\n\n    public void setCollapsedTextAppearance(int resId) {\n        TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.QMUITextAppearance);\n        if (a.hasValue(R.styleable.QMUITextAppearance_android_textColor)) {\n            mCollapsedTextColor = a.getColorStateList(R.styleable.QMUITextAppearance_android_textColor);\n        }\n        if (a.hasValue(R.styleable.QMUITextAppearance_android_textSize)) {\n            mCollapsedTextSize = a.getDimensionPixelSize(R.styleable.QMUITextAppearance_android_textSize,\n                    (int) mCollapsedTextSize);\n        }\n        mCollapsedShadowColor = a.getInt(R.styleable.QMUITextAppearance_android_shadowColor, 0);\n        mCollapsedShadowDx = a.getFloat(R.styleable.QMUITextAppearance_android_shadowDx, 0);\n        mCollapsedShadowDy = a.getFloat(R.styleable.QMUITextAppearance_android_shadowDy, 0);\n        mCollapsedShadowRadius = a.getFloat(R.styleable.QMUITextAppearance_android_shadowRadius, 0);\n        a.recycle();\n\n        mCollapsedTypeface = readFontFamilyTypeface(resId);\n\n        recalculate();\n    }\n\n    public void setExpandedTextAppearance(int resId) {\n        TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.QMUITextAppearance);\n        if (a.hasValue(R.styleable.QMUITextAppearance_android_textColor)) {\n            mExpandedTextColor = a.getColorStateList(R.styleable.QMUITextAppearance_android_textColor);\n        }\n        if (a.hasValue(R.styleable.QMUITextAppearance_android_textSize)) {\n            mExpandedTextSize = a.getDimensionPixelSize(R.styleable.QMUITextAppearance_android_textSize,\n                    (int) mExpandedTextSize);\n        }\n        mExpandedShadowColor = a.getInt(\n                R.styleable.QMUITextAppearance_android_shadowColor, 0);\n        mExpandedShadowDx = a.getFloat(\n                R.styleable.QMUITextAppearance_android_shadowDx, 0);\n        mExpandedShadowDy = a.getFloat(\n                R.styleable.QMUITextAppearance_android_shadowDy, 0);\n        mExpandedShadowRadius = a.getFloat(\n                R.styleable.QMUITextAppearance_android_shadowRadius, 0);\n        a.recycle();\n\n        mExpandedTypeface = readFontFamilyTypeface(resId);\n\n        recalculate();\n    }\n\n    public void setExpandedBounds(int left, int top, int right, int bottom) {\n        if (!rectEquals(mExpandedBounds, left, top, right, bottom)) {\n            mExpandedBounds.set(left, top, right, bottom);\n            mBoundsChanged = true;\n            onBoundsChanged();\n        }\n    }\n\n    public void setCollapsedBounds(int left, int top, int right, int bottom) {\n        if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) {\n            mCollapsedBounds.set(left, top, right, bottom);\n            mBoundsChanged = true;\n            onBoundsChanged();\n        }\n    }\n\n    void onBoundsChanged() {\n        mDrawTitle = mCollapsedBounds.width() > 0 && mCollapsedBounds.height() > 0\n                && mExpandedBounds.width() > 0 && mExpandedBounds.height() > 0;\n    }\n\n    public void setExpandedTextGravity(int gravity) {\n        if (mExpandedTextGravity != gravity) {\n            mExpandedTextGravity = gravity;\n            recalculate();\n        }\n    }\n\n    public int getExpandedTextGravity() {\n        return mExpandedTextGravity;\n    }\n\n    public void setCollapsedTextGravity(int gravity) {\n        if (mCollapsedTextGravity != gravity) {\n            mCollapsedTextGravity = gravity;\n            recalculate();\n        }\n    }\n\n    public int getCollapsedTextGravity() {\n        return mCollapsedTextGravity;\n    }\n\n    public void setGravity(int collapsedGravity, int expandedGravity, boolean recalculate){\n        if(mCollapsedTextGravity != collapsedGravity || mExpandedTextGravity != expandedGravity){\n            mCollapsedTextGravity = collapsedGravity;\n            mExpandedTextGravity = expandedGravity;\n            if(recalculate){\n                recalculate();\n            }\n        }\n    }\n\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)\n    private Typeface readFontFamilyTypeface(int resId) {\n        final TypedArray a = mView.getContext().obtainStyledAttributes(resId,\n                new int[]{android.R.attr.fontFamily});\n        try {\n            final String family = a.getString(0);\n            if (family != null) {\n                return Typeface.create(family, Typeface.NORMAL);\n            }\n        } finally {\n            a.recycle();\n        }\n        return null;\n    }\n\n    public void setTypeface(Typeface collapsedTypeface, Typeface expandedTypeface, boolean recalculate){\n        if(mCollapsedTypeface != collapsedTypeface || mExpandedTypeface != expandedTypeface){\n            mCollapsedTypeface = collapsedTypeface;\n            mExpandedTypeface = expandedTypeface;\n            if(recalculate){\n                recalculate();\n            }\n        }\n    }\n\n    public void setCollapsedTypeface(Typeface typeface) {\n        if (mCollapsedTypeface != typeface) {\n            mCollapsedTypeface = typeface;\n            recalculate();\n        }\n    }\n\n    public void setExpandedTypeface(Typeface typeface) {\n        if (mExpandedTypeface != typeface) {\n            mExpandedTypeface = typeface;\n            recalculate();\n        }\n    }\n\n    public void setTypefaces(Typeface typeface) {\n        mCollapsedTypeface = mExpandedTypeface = typeface;\n        recalculate();\n    }\n\n    public Typeface getCollapsedTypeface() {\n        return mCollapsedTypeface != null ? mCollapsedTypeface : Typeface.DEFAULT;\n    }\n\n    public Typeface getExpandedTypeface() {\n        return mExpandedTypeface != null ? mExpandedTypeface : Typeface.DEFAULT;\n    }\n\n    /**\n     * Set the value indicating the current scroll value. This decides how much of the\n     * background will be displayed, as well as the title metrics/positioning.\n     * <p>\n     * A value of {@code 0.0} indicates that the layout is fully expanded.\n     * A value of {@code 1.0} indicates that the layout is fully collapsed.\n     */\n    public void setExpansionFraction(float fraction) {\n        fraction = QMUILangHelper.constrain(fraction, 0f, 1f);\n\n        if (fraction != mExpandedFraction) {\n            mExpandedFraction = fraction;\n            calculateCurrentOffsets();\n        }\n    }\n\n    public final boolean setState(final int[] state) {\n        mState = state;\n\n        if (isStateful()) {\n            recalculate();\n            return true;\n        }\n\n        return false;\n    }\n\n    public void setTypefaceUpdateAreaPercent(float typefaceUpdateAreaPercent) {\n        mTypefaceUpdateAreaPercent = typefaceUpdateAreaPercent;\n    }\n\n    public final boolean isStateful() {\n        return (mCollapsedTextColor != null && mCollapsedTextColor.isStateful())\n                || (mExpandedTextColor != null && mExpandedTextColor.isStateful());\n    }\n\n    public float getExpansionFraction() {\n        return mExpandedFraction;\n    }\n\n    public float getCollapsedTextSize() {\n        return mCollapsedTextSize;\n    }\n\n    public float getExpandedTextSize() {\n        return mExpandedTextSize;\n    }\n\n    public void calculateCurrentOffsets() {\n        calculateOffsets(mExpandedFraction);\n    }\n\n    private void calculateOffsets(final float fraction) {\n        interpolateBounds(fraction);\n        mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction,\n                mPositionInterpolator);\n        mCurrentDrawY = lerp(mExpandedDrawY, mCollapsedDrawY, fraction,\n                mPositionInterpolator);\n        mCurrentTextHeight = lerp(mExpandedTextHeight, mCollapsedTextHeight, fraction,\n                mPositionInterpolator);\n        mCurrentTextWidth = lerp(mExpandedTextWidth, mCollapsedTextWidth, fraction,\n                mPositionInterpolator);\n\n        setInterpolatedTextSize(lerp(mExpandedTextSize, mCollapsedTextSize,\n                fraction, mTextSizeInterpolator));\n\n        if (mCollapsedTextColor != mExpandedTextColor) {\n            // If the collapsed and expanded text colors are different, blend them based on the\n            // fraction\n            mTextPaint.setColor(QMUIColorHelper.computeColor(\n                    getCurrentExpandedTextColor(), getCurrentCollapsedTextColor(), fraction));\n        } else {\n            mTextPaint.setColor(getCurrentCollapsedTextColor());\n        }\n\n        mTextPaint.setShadowLayer(\n                lerp(mExpandedShadowRadius, mCollapsedShadowRadius, fraction, null),\n                lerp(mExpandedShadowDx, mCollapsedShadowDx, fraction, null),\n                lerp(mExpandedShadowDy, mCollapsedShadowDy, fraction, null),\n                QMUIColorHelper.computeColor(mExpandedShadowColor, mCollapsedShadowColor, fraction));\n\n        ViewCompat.postInvalidateOnAnimation(mView);\n    }\n\n    @ColorInt\n    private int getCurrentExpandedTextColor() {\n        if(mExpandedTextColor == null){\n            return 0;\n        }\n        if (mState != null) {\n            return mExpandedTextColor.getColorForState(mState, 0);\n        } else {\n            return mExpandedTextColor.getDefaultColor();\n        }\n    }\n\n    @ColorInt\n    private int getCurrentCollapsedTextColor() {\n        if(mCollapsedTextColor == null){\n            return 0;\n        }\n        if (mState != null) {\n            return mCollapsedTextColor.getColorForState(mState, 0);\n        } else {\n            return mCollapsedTextColor.getDefaultColor();\n        }\n    }\n\n    public void calculateBaseOffsets() {\n        final float currentTextSize = mCurrentTextSize;\n\n        // We then calculate the collapsed text size, using the same logic\n        calculateUsingTextSize(mCollapsedTextSize);\n        mCollapsedTextWidth = mTextToDraw != null ?\n                mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;\n        mCollapsedTextHeight = mTextPaint.descent() - mTextPaint.ascent();\n        final int collapsedAbsGravity = GravityCompat.getAbsoluteGravity(mCollapsedTextGravity,\n                mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);\n        switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n            case Gravity.BOTTOM:\n                mCollapsedDrawY = mCollapsedBounds.bottom - mTextPaint.descent();\n                break;\n            case Gravity.TOP:\n                mCollapsedDrawY = mCollapsedBounds.top - mTextPaint.ascent();\n                break;\n            case Gravity.CENTER_VERTICAL:\n            default:\n                float textOffset = (mCollapsedTextHeight / 2) - mTextPaint.descent();\n                mCollapsedDrawY = mCollapsedBounds.centerY() + textOffset;\n                break;\n        }\n        switch (collapsedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n            case Gravity.CENTER_HORIZONTAL:\n                mCollapsedDrawX = mCollapsedBounds.centerX() - (mCollapsedTextWidth / 2);\n                break;\n            case Gravity.RIGHT:\n                mCollapsedDrawX = mCollapsedBounds.right - mCollapsedTextWidth;\n                break;\n            case Gravity.LEFT:\n            default:\n                mCollapsedDrawX = mCollapsedBounds.left;\n                break;\n        }\n\n        calculateUsingTextSize(mExpandedTextSize);\n        mExpandedTextWidth = mTextToDraw != null\n                ? mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;\n        mExpandedTextHeight = mTextPaint.descent() - mTextPaint.ascent();\n        final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(mExpandedTextGravity,\n                mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);\n        switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n            case Gravity.BOTTOM:\n                mExpandedDrawY = mExpandedBounds.bottom - mTextPaint.descent();\n                break;\n            case Gravity.TOP:\n                mExpandedDrawY = mExpandedBounds.top - mTextPaint.ascent();\n                break;\n            case Gravity.CENTER_VERTICAL:\n            default:\n                float textOffset = (mExpandedTextHeight / 2) - mTextPaint.descent();\n                mExpandedDrawY = mExpandedBounds.centerY() + textOffset;\n                break;\n        }\n        switch (expandedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n            case Gravity.CENTER_HORIZONTAL:\n                mExpandedDrawX = mExpandedBounds.centerX() - (mExpandedTextWidth / 2);\n                break;\n            case Gravity.RIGHT:\n                mExpandedDrawX = mExpandedBounds.right - mExpandedTextWidth;\n                break;\n            case Gravity.LEFT:\n            default:\n                mExpandedDrawX = mExpandedBounds.left;\n                break;\n        }\n\n        // The bounds have changed so we need to clear the texture\n        clearTexture();\n        // Now reset the text size back to the original\n        setInterpolatedTextSize(currentTextSize);\n    }\n\n    private void interpolateBounds(float fraction) {\n        mCurrentBounds.left = lerp(mExpandedBounds.left, mCollapsedBounds.left,\n                fraction, mPositionInterpolator);\n        mCurrentBounds.top = lerp(mExpandedDrawY, mCollapsedDrawY,\n                fraction, mPositionInterpolator);\n        mCurrentBounds.right = lerp(mExpandedBounds.right, mCollapsedBounds.right,\n                fraction, mPositionInterpolator);\n        mCurrentBounds.bottom = lerp(mExpandedBounds.bottom, mCollapsedBounds.bottom,\n                fraction, mPositionInterpolator);\n    }\n\n    // 系统类原有代码，不作检测\n    @SuppressWarnings(\"UnusedAssignment\")\n    public void draw(Canvas canvas) {\n        final int saveCount = canvas.save();\n\n        if (mTextToDraw != null && mDrawTitle) {\n            float x = mCurrentDrawX;\n            float y = mCurrentDrawY;\n\n            final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;\n\n            final float ascent;\n            final float descent;\n            if (drawTexture) {\n                ascent = mTextureAscent * mScale;\n                descent = mTextureDescent * mScale;\n            } else {\n                ascent = mTextPaint.ascent() * mScale;\n                descent = mTextPaint.descent() * mScale;\n            }\n\n            if (DEBUG_DRAW) {\n                // Just a debug tool, which drawn a magenta rect in the text bounds\n                canvas.drawRect(mCurrentBounds.left, y + ascent, mCurrentBounds.right, y + descent,\n                        DEBUG_DRAW_PAINT);\n            }\n\n            if (drawTexture) {\n                y += ascent;\n            }\n\n            if (mScale != 1f) {\n                canvas.scale(mScale, mScale, x, y);\n            }\n\n            if (drawTexture) {\n                // If we should use a texture, draw it instead of text\n                canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);\n            } else {\n                canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint);\n            }\n        }\n\n        canvas.restoreToCount(saveCount);\n    }\n\n    private boolean calculateIsRtl(CharSequence text) {\n        final boolean defaultIsRtl = ViewCompat.getLayoutDirection(mView)\n                == ViewCompat.LAYOUT_DIRECTION_RTL;\n        return (defaultIsRtl\n                ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL\n                : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length());\n    }\n\n    private void setInterpolatedTextSize(float textSize) {\n        calculateUsingTextSize(textSize);\n\n        // Use our texture if the scale isn't 1.0\n        mUseTexture = USE_SCALING_TEXTURE && mScale != 1f;\n\n        if (mUseTexture) {\n            // Make sure we have an expanded texture if needed\n            ensureExpandedTexture();\n        }\n\n        ViewCompat.postInvalidateOnAnimation(mView);\n    }\n\n    private void calculateUsingTextSize(final float textSize) {\n        if (mText == null) return;\n\n        final float collapsedWidth = mCollapsedBounds.width();\n        final float expandedWidth = mExpandedBounds.width();\n\n        final float availableWidth;\n        final float newTextSize;\n        boolean updateDrawText = false;\n\n        if(mExpandedFraction >= 1f - mTypefaceUpdateAreaPercent){\n            if (mCurrentTypeface != mCollapsedTypeface) {\n                mCurrentTypeface = mCollapsedTypeface;\n                updateDrawText = true;\n            }\n        }else if(mExpandedFraction <= mTypefaceUpdateAreaPercent){\n            if (mCurrentTypeface != mExpandedTypeface) {\n                mCurrentTypeface = mExpandedTypeface;\n                updateDrawText = true;\n            }\n        }\n\n        if (isClose(textSize, mCollapsedTextSize)) {\n            newTextSize = mCollapsedTextSize;\n            mScale = 1f;\n            availableWidth = collapsedWidth;\n        } else {\n            newTextSize = mExpandedTextSize;\n            if (isClose(textSize, mExpandedTextSize)) {\n                // If we're close to the expanded text size, snap to it and use a scale of 1\n                mScale = 1f;\n            } else {\n                // Else, we'll scale down from the expanded text size\n                mScale = textSize / mExpandedTextSize;\n            }\n\n            final float textSizeRatio = mCollapsedTextSize / mExpandedTextSize;\n            // This is the size of the expanded bounds when it is scaled to match the\n            // collapsed text size\n            final float scaledDownWidth = expandedWidth * textSizeRatio;\n\n            if (scaledDownWidth > collapsedWidth) {\n                // If the scaled down size is larger than the actual collapsed width, we need to\n                // cap the available width so that when the expanded text scales down, it matches\n                // the collapsed width\n                availableWidth = Math.min(collapsedWidth / textSizeRatio, expandedWidth);\n            } else {\n                // Otherwise we'll just use the expanded width\n                availableWidth = expandedWidth;\n            }\n        }\n\n        if (availableWidth > 0) {\n            updateDrawText = (mCurrentTextSize != newTextSize) || mBoundsChanged || updateDrawText;\n            mCurrentTextSize = newTextSize;\n            mBoundsChanged = false;\n        }\n\n        if (mTextToDraw == null || updateDrawText) {\n            mTextPaint.setTextSize(mCurrentTextSize);\n            mTextPaint.setTypeface(mCurrentTypeface);\n            // Use linear text scaling if we're scaling the canvas\n            mTextPaint.setLinearText(mScale != 1f);\n\n            // If we don't currently have text to draw, or the text size has changed, ellipsize...\n            final CharSequence title = TextUtils.ellipsize(mText, mTextPaint,\n                    availableWidth, TextUtils.TruncateAt.END);\n            if (!TextUtils.equals(title, mTextToDraw)) {\n                mTextToDraw = title;\n                mIsRtl = calculateIsRtl(mTextToDraw);\n            }\n        }\n    }\n\n    private void ensureExpandedTexture() {\n        if (mExpandedTitleTexture != null || mExpandedBounds.isEmpty()\n                || TextUtils.isEmpty(mTextToDraw)) {\n            return;\n        }\n\n        calculateOffsets(0f);\n        mTextureAscent = mTextPaint.ascent();\n        mTextureDescent = mTextPaint.descent();\n\n        final int w = Math.round(mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()));\n        final int h = Math.round(mTextureDescent - mTextureAscent);\n\n        if (w <= 0 || h <= 0) {\n            return; // If the width or height are 0, return\n        }\n\n        mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\n\n        Canvas c = new Canvas(mExpandedTitleTexture);\n        c.drawText(mTextToDraw, 0, mTextToDraw.length(), 0, h - mTextPaint.descent(), mTextPaint);\n\n        if (mTexturePaint == null) {\n            // Make sure we have a paint\n            mTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);\n        }\n    }\n\n    public void recalculate() {\n        if (mView.getHeight() > 0 && mView.getWidth() > 0) {\n            // If we've already been laid out, calculate everything now otherwise we'll wait\n            // until a layout\n            calculateBaseOffsets();\n            calculateCurrentOffsets();\n        }\n    }\n\n    /**\n     * Set the title to display\n     *\n     * @param text content of title\n     */\n    public void setText(CharSequence text) {\n        if (text == null || !text.equals(mText)) {\n            mText = text;\n            mTextToDraw = null;\n            clearTexture();\n            recalculate();\n        }\n    }\n\n    public CharSequence getText() {\n        return mText;\n    }\n\n    private void clearTexture() {\n        if (mExpandedTitleTexture != null) {\n            mExpandedTitleTexture.recycle();\n            mExpandedTitleTexture = null;\n        }\n    }\n\n    public float getExpandedTextWidth() {\n        return mExpandedTextWidth;\n    }\n\n    public float getCollapsedTextWidth() {\n        return mCollapsedTextWidth;\n    }\n\n    public float getExpandedTextHeight() {\n        return mExpandedTextHeight;\n    }\n\n    public float getCollapsedTextHeight() {\n        return mCollapsedTextHeight;\n    }\n\n    public float getExpandedDrawX() {\n        return mExpandedDrawX;\n    }\n\n    public float getCollapsedDrawX() {\n        return mCollapsedDrawX;\n    }\n\n\n    /**\n     * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently\n     * defined as it's difference being < 0.001.\n     */\n    private static boolean isClose(float value, float targetValue) {\n        return Math.abs(value - targetValue) < 0.001f;\n    }\n\n    ColorStateList getExpandedTextColor() {\n        return mExpandedTextColor;\n    }\n\n    ColorStateList getCollapsedTextColor() {\n        return mCollapsedTextColor;\n    }\n\n    public static float lerp(float startValue, float endValue, float fraction,\n                              Interpolator interpolator) {\n        if (interpolator != null) {\n            fraction = interpolator.getInterpolation(fraction);\n        }\n        return startValue + Math.round(fraction * (endValue - startValue));\n    }\n\n    private static boolean rectEquals(Rect r, int left, int top, int right, int bottom) {\n        return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIColorHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.graphics.Color;\n\nimport androidx.annotation.ColorInt;\n\n/**\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUIColorHelper {\n\n    public static int setColorAlpha(@ColorInt int color, float alpha) {\n        return setColorAlpha(color, alpha, true);\n    }\n\n    /**\n     * 设置颜色的alpha值\n     *\n     * @param color    需要被设置的颜色值\n     * @param alpha    取值为[0,1]，0表示全透明，1表示不透明\n     * @param override 覆盖原本的 alpha\n     * @return 返回改变了 alpha 值的颜色值\n     */\n    public static int setColorAlpha(@ColorInt int color, float alpha, boolean override) {\n        int origin = override ? 0xff : (color >> 24) & 0xff;\n        return color & 0x00ffffff | (int) (alpha * origin) << 24;\n    }\n\n    /**\n     * 根据比例，在两个color值之间计算出一个color值\n     * <b>注意该方法是ARGB通道分开计算比例的</b>\n     *\n     * @param fromColor 开始的color值\n     * @param toColor   最终的color值\n     * @param fraction  比例，取值为[0,1]，为0时返回 fromColor， 为1时返回 toColor\n     * @return 计算出的color值\n     */\n    public static int computeColor(@ColorInt int fromColor, @ColorInt int toColor, float fraction) {\n        fraction = QMUILangHelper.constrain(fraction, 0f, 1f);\n\n        int minColorA = Color.alpha(fromColor);\n        int maxColorA = Color.alpha(toColor);\n        int resultA = (int) ((maxColorA - minColorA) * fraction) + minColorA;\n\n        int minColorR = Color.red(fromColor);\n        int maxColorR = Color.red(toColor);\n        int resultR = (int) ((maxColorR - minColorR) * fraction) + minColorR;\n\n        int minColorG = Color.green(fromColor);\n        int maxColorG = Color.green(toColor);\n        int resultG = (int) ((maxColorG - minColorG) * fraction) + minColorG;\n\n        int minColorB = Color.blue(fromColor);\n        int maxColorB = Color.blue(toColor);\n        int resultB = (int) ((maxColorB - minColorB) * fraction) + minColorB;\n\n        return Color.argb(resultA, resultR, resultG, resultB);\n    }\n\n    /**\n     * 将 color 颜色值转换为十六进制字符串\n     *\n     * @param color 颜色值\n     * @return 转换后的字符串\n     */\n    public static String colorToString(@ColorInt int color) {\n        return String.format(\"#%08X\", color);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIDeviceHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActivityManager;\nimport android.app.AppOpsManager;\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.StatFs;\nimport android.provider.Settings;\nimport android.text.TextUtils;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.QMUILog;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Method;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @author cginechen\n * @date 2016-08-11\n */\n@SuppressLint(\"PrivateApi\")\npublic class QMUIDeviceHelper {\n    private final static String TAG = \"QMUIDeviceHelper\";\n    private final static String KEY_MIUI_VERSION_NAME = \"ro.miui.ui.version.name\";\n    private static final String KEY_FLYME_VERSION_NAME = \"ro.build.display.id\";\n    private final static String FLYME = \"flyme\";\n    private final static String ZTEC2016 = \"zte c2016\";\n    private final static String ZUKZ1 = \"zuk z1\";\n    private final static String MEIZUBOARD[] = {\"m9\", \"M9\", \"mx\", \"MX\"};\n    private final static String POWER_PROFILE_CLASS = \"com.android.internal.os.PowerProfile\";\n    private final static String CPU_FILE_PATH_0 = \"/sys/devices/system/cpu/\";\n    private final static String CPU_FILE_PATH_1 = \"/sys/devices/system/cpu/possible\";\n    private final static String CPU_FILE_PATH_2 = \"/sys/devices/system/cpu/present\";\n    private static FileFilter CPU_FILTER = new FileFilter() {\n\n        @Override\n        public boolean accept(File pathname) {\n            return Pattern.matches(\"cpu[0-9]\", pathname.getName());\n        }\n    };\n\n    private static String sMiuiVersionName;\n    private static String sFlymeVersionName;\n    private static boolean sIsTabletChecked = false;\n    private static boolean sIsTabletValue = false;\n    private static final String BRAND = Build.BRAND.toLowerCase();\n    private static long sTotalMemory = -1;\n    private static long sInnerStorageSize = -1;\n    private static long sExtraStorageSize = -1;\n    private static double sBatteryCapacity = -1;\n    private static int sCpuCoreCount = -1;\n    private static boolean isInfoReaded = false;\n\n    private static void checkReadInfo(){\n        if(isInfoReaded){\n            return;\n        }\n        isInfoReaded = true;\n        Properties properties = new Properties();\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n            // android 8.0，读取 /system/uild.prop 会报 permission denied\n            FileInputStream fileInputStream = null;\n            try {\n                fileInputStream = new FileInputStream(new File(Environment.getRootDirectory(), \"build.prop\"));\n                properties.load(fileInputStream);\n            } catch (Exception e) {\n                QMUILog.printErrStackTrace(TAG, e, \"read file error\");\n            } finally {\n                QMUILangHelper.close(fileInputStream);\n            }\n        }\n\n        Class<?> clzSystemProperties = null;\n        try {\n            clzSystemProperties = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = clzSystemProperties.getDeclaredMethod(\"get\", String.class);\n            // miui\n            sMiuiVersionName = getLowerCaseName(properties, getMethod, KEY_MIUI_VERSION_NAME);\n            //flyme\n            sFlymeVersionName = getLowerCaseName(properties, getMethod, KEY_FLYME_VERSION_NAME);\n        } catch (Exception e) {\n            QMUILog.printErrStackTrace(TAG, e, \"read SystemProperties error\");\n        }\n    }\n\n    private static boolean _isTablet(Context context) {\n        return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >=\n                Configuration.SCREENLAYOUT_SIZE_LARGE;\n    }\n\n    /**\n     * 判断是否为平板设备\n     */\n    public static boolean isTablet(Context context) {\n        if (sIsTabletChecked) {\n            return sIsTabletValue;\n        }\n        sIsTabletValue = _isTablet(context);\n        sIsTabletChecked = true;\n        return sIsTabletValue;\n    }\n\n    /**\n     * 判断是否是flyme系统\n     */\n    private static OnceReadValue<Void, Boolean> isFlymeValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            checkReadInfo();\n            return !TextUtils.isEmpty(sFlymeVersionName) && sFlymeVersionName.contains(FLYME);\n        }\n    };\n    public static boolean isFlyme() {\n        return isFlymeValue.get(null);\n    }\n\n    /**\n     * 判断是否是MIUI系统\n     */\n    public static boolean isMIUI() {\n        checkReadInfo();\n        return !TextUtils.isEmpty(sMiuiVersionName);\n    }\n\n    public static boolean isMIUIV5() {\n        checkReadInfo();\n        return \"v5\".equals(sMiuiVersionName);\n    }\n\n    public static boolean isMIUIV6() {\n        checkReadInfo();\n        return \"v6\".equals(sMiuiVersionName);\n    }\n\n    public static boolean isMIUIV7() {\n        checkReadInfo();\n        return \"v7\".equals(sMiuiVersionName);\n    }\n\n    public static boolean isMIUIV8() {\n        checkReadInfo();\n        return \"v8\".equals(sMiuiVersionName);\n    }\n\n    public static boolean isMIUIV9() {\n        checkReadInfo();\n        return \"v9\".equals(sMiuiVersionName);\n    }\n\n    public static boolean isFlymeLowerThan(int majorVersion) {\n        return isFlymeLowerThan(majorVersion, 0, 0);\n    }\n\n    public static boolean isFlymeLowerThan(int majorVersion, int minorVersion, int patchVersion) {\n        checkReadInfo();\n        boolean isLower = false;\n        if (sFlymeVersionName != null && !sFlymeVersionName.equals(\"\")) {\n            try {\n                Pattern pattern = Pattern.compile(\"(\\\\d+\\\\.){2}\\\\d\");\n                Matcher matcher = pattern.matcher(sFlymeVersionName);\n                if (matcher.find()) {\n                    String versionString = matcher.group();\n                    if (versionString.length() > 0) {\n                        String[] version = versionString.split(\"\\\\.\");\n                        if (version.length >= 1) {\n                            if (Integer.parseInt(version[0]) < majorVersion) {\n                                isLower = true;\n                            }\n                        }\n\n                        if (version.length >= 2 && minorVersion > 0) {\n                            if (Integer.parseInt(version[1]) < majorVersion) {\n                                isLower = true;\n                            }\n                        }\n\n                        if (version.length >= 3 && patchVersion > 0) {\n                            if (Integer.parseInt(version[2]) < majorVersion) {\n                                isLower = true;\n                            }\n                        }\n                    }\n                }\n            } catch (Throwable ignore) {\n\n            }\n        }\n        return isMeizu() && isLower;\n    }\n\n\n    private static OnceReadValue<Void, Boolean> isMeizuValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            checkReadInfo();\n            return isPhone(MEIZUBOARD) || isFlyme();\n        }\n    };\n    public static boolean isMeizu() {\n        return isMeizuValue.get(null);\n    }\n\n    /**\n     * 判断是否为小米\n     * https://dev.mi.com/doc/?p=254\n     */\n    private static OnceReadValue<Void, Boolean> isXiaomiValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            return Build.MANUFACTURER.toLowerCase().equals(\"xiaomi\");\n        }\n    };\n    public static boolean isXiaomi() {\n        return isXiaomiValue.get(null);\n    }\n\n    private static OnceReadValue<Void, Boolean> isVivoValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            return BRAND.contains(\"vivo\") || BRAND.contains(\"bbk\");\n        }\n    };\n    public static boolean isVivo() {\n        return isVivoValue.get(null);\n    }\n\n    private static OnceReadValue<Void, Boolean> isOppoValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            return BRAND.contains(\"oppo\");\n        }\n    };\n    public static boolean isOppo() {\n        return isOppoValue.get(null);\n    }\n\n    private static OnceReadValue<Void, Boolean> isHuaweiValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            return BRAND.contains(\"huawei\") || BRAND.contains(\"honor\");\n        }\n    };\n    public static boolean isHuawei() {\n        return isHuaweiValue.get(null);\n    }\n\n    private static OnceReadValue<Void, Boolean> isEssentialPhoneValue = new OnceReadValue<Void, Boolean>() {\n        @Override\n        protected Boolean read(Void param) {\n            return BRAND.contains(\"essential\");\n        }\n    };\n    public static boolean isEssentialPhone() {\n        return isEssentialPhoneValue.get(null);\n    }\n\n    private static OnceReadValue<Context, Boolean> isMiuiFullDisplayValue = new OnceReadValue<Context, Boolean>() {\n        @Override\n        protected Boolean read(Context param) {\n            return isMIUI() && Settings.Global.getInt(param.getContentResolver(), \"force_fsg_nav_bar\", 0) != 0;\n        }\n    };\n    public static boolean isMiuiFullDisplay(Context context){\n        return isMiuiFullDisplayValue.get(context);\n    }\n\n    private static boolean isPhone(String[] boards) {\n        checkReadInfo();\n        final String board = android.os.Build.BOARD;\n        if (board == null) {\n            return false;\n        }\n        for (String board1 : boards) {\n            if (board.equals(board1)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public static long getTotalMemory(Context context) {\n        if (sTotalMemory != -1) {\n            return sTotalMemory;\n        }\n        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();\n        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (activityManager != null) {\n            activityManager.getMemoryInfo(memoryInfo);\n            sTotalMemory = memoryInfo.totalMem;\n        }\n        return sTotalMemory;\n    }\n\n    public static long getInnerStorageSize() {\n        if (sInnerStorageSize != -1) {\n            return sInnerStorageSize;\n        }\n        File dataDir = Environment.getDataDirectory();\n        if (dataDir == null) {\n            return 0;\n        }\n        sInnerStorageSize = dataDir.getTotalSpace();\n        return sInnerStorageSize;\n    }\n\n\n    public static boolean hasExtraStorage() {\n        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());\n    }\n\n\n    public static long getExtraStorageSize() {\n        if (sExtraStorageSize != -1) {\n            return sExtraStorageSize;\n        }\n        if (!hasExtraStorage()) {\n            return 0;\n        }\n        File path = Environment.getExternalStorageDirectory();\n        StatFs stat = new StatFs(path.getPath());\n        long blockSize = stat.getBlockSizeLong();\n        long availableBlocks = stat.getBlockCountLong();\n        sExtraStorageSize = blockSize * availableBlocks;\n        return sExtraStorageSize;\n    }\n\n    public static long getTotalStorageSize() {\n        return getInnerStorageSize() + getExtraStorageSize();\n    }\n\n    // From Matrix\n    public static int getCpuCoreCount() {\n        if (sCpuCoreCount != -1) {\n            return sCpuCoreCount;\n        }\n        int cores;\n        try {\n            cores = getCoresFromFile(CPU_FILE_PATH_1);\n            if (cores == 0) {\n                cores = getCoresFromFile(CPU_FILE_PATH_2);\n            }\n            if (cores == 0) {\n                cores = getCoresFromCPUFiles(CPU_FILE_PATH_0);\n            }\n        } catch (Exception e) {\n            cores = 0;\n        }\n        if (cores == 0) {\n            cores = 1;\n        }\n        sCpuCoreCount = cores;\n        return cores;\n    }\n\n    private static int getCoresFromCPUFiles(String path) {\n        File[] list = new File(path).listFiles(CPU_FILTER);\n        return null == list ? 0 : list.length;\n    }\n\n    private static int getCoresFromFile(String file) {\n        InputStream is = null;\n        try {\n            is = new FileInputStream(file);\n            BufferedReader buf = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));\n            String fileContents = buf.readLine();\n            buf.close();\n            if (fileContents == null || !fileContents.matches(\"0-[\\\\d]+$\")) {\n                return 0;\n            }\n            String num = fileContents.substring(2);\n            return Integer.parseInt(num) + 1;\n        } catch (IOException e) {\n            return 0;\n        } finally {\n            QMUILangHelper.close(is);\n        }\n    }\n\n    /**\n     * 判断悬浮窗权限（目前主要用户魅族与小米的检测）。\n     */\n    public static boolean isFloatWindowOpAllowed(Context context) {\n        final int version = Build.VERSION.SDK_INT;\n        return checkOp(context, 24);  // 24 是AppOpsManager.OP_SYSTEM_ALERT_WINDOW 的值，该值无法直接访问\n    }\n\n    public static double getBatteryCapacity(Context context) {\n        if (sBatteryCapacity != -1) {\n            return sBatteryCapacity;\n        }\n        double ret;\n        try {\n            Class<?> cls = Class.forName(POWER_PROFILE_CLASS);\n            Object instance = cls.getConstructor(Context.class).newInstance(context);\n            Method method = cls.getMethod(\"getBatteryCapacity\");\n            ret = (double) method.invoke(instance);\n        } catch (Exception ignore) {\n            ret = -1;\n        }\n        sBatteryCapacity = ret;\n        return sBatteryCapacity;\n    }\n\n\n    private static boolean checkOp(Context context, int op) {\n        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);\n        try {\n            Method method = manager.getClass().getDeclaredMethod(\"checkOp\", int.class, int.class, String.class);\n            int property = (Integer) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());\n            return AppOpsManager.MODE_ALLOWED == property;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    @Nullable\n    private static String getLowerCaseName(Properties p, Method get, String key) {\n        String name = p.getProperty(key);\n        if (name == null) {\n            try {\n                name = (String) get.invoke(null, key);\n            } catch (Exception ignored) {\n            }\n        }\n        if (name != null) name = name.toLowerCase();\n        return name;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIDirection.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\n/**\n * 定义了从左到右，从上到下，从右到左，从下到上四个方向的类\n * Created by Kayo on 2017/2/7.\n */\n\npublic enum QMUIDirection {\n    LEFT_TO_RIGHT,\n    TOP_TO_BOTTOM,\n    RIGHT_TO_LEFT,\n    BOTTOM_TO_TOP\n}\n\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIDisplayHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.graphics.Point;\nimport android.net.ConnectivityManager;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.Settings;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.Display;\nimport android.view.KeyCharacterMap;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Locale;\n\n/**\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUIDisplayHelper {\n\n    /**\n     * 屏幕密度,系统源码注释不推荐使用\n     */\n    public static final float DENSITY = Resources.getSystem()\n            .getDisplayMetrics().density;\n    private static final String TAG = \"QMUIDisplayHelper\";\n\n    /**\n     * 是否有摄像头\n     */\n    private static Boolean sHasCamera = null;\n\n//    private static int[] sPortraitRealSizeCache = null;\n//    private static int[] sLandscapeRealSizeCache = null;\n\n    /**\n     * 获取 DisplayMetrics\n     *\n     * @return\n     */\n    public static DisplayMetrics getDisplayMetrics(Context context) {\n        return context.getResources().getDisplayMetrics();\n    }\n\n    /**\n     * 把以 dp 为单位的值，转化为以 px 为单位的值\n     *\n     * @param dpValue 以 dp 为单位的值\n     * @return px value\n     */\n    public static int dpToPx(int dpValue) {\n        return (int) (dpValue * DENSITY + 0.5f);\n    }\n\n    /**\n     * 把以 px 为单位的值，转化为以 dp 为单位的值\n     *\n     * @param pxValue 以 px 为单位的值\n     * @return dp值\n     */\n    public static int pxToDp(float pxValue) {\n        return (int) (pxValue / DENSITY + 0.5f);\n    }\n\n    public static float getDensity(Context context) {\n        return context.getResources().getDisplayMetrics().density;\n    }\n\n    public static float getFontDensity(Context context) {\n        return context.getResources().getDisplayMetrics().scaledDensity;\n    }\n\n    /**\n     * 获取屏幕宽度\n     *\n     * @return\n     */\n    public static int getScreenWidth(Context context) {\n        return getDisplayMetrics(context).widthPixels;\n    }\n\n    /**\n     * 获取屏幕高度\n     *\n     * @return\n     */\n    public static int getScreenHeight(Context context) {\n        int screenHeight = getDisplayMetrics(context).heightPixels;\n        if(QMUIDeviceHelper.isXiaomi() && xiaomiNavigationGestureEnabled(context)){\n            screenHeight += getResourceNavHeight(context);\n        }\n        return screenHeight;\n    }\n\n    /**\n     * 获取屏幕的真实宽高\n     *\n     * @param context\n     * @return\n     */\n\n    public static int[] getRealScreenSize(Context context) {\n        // 切换屏幕导致宽高变化时不能用 cache，先去掉 cache\n        return doGetRealScreenSize(context);\n//        if (QMUIDeviceHelper.isEssentialPhone() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n//            // Essential Phone 8.0版本后，Display size 会根据挖孔屏的设置而得到不同的结果，不能信任 cache\n//            return doGetRealScreenSize(context);\n//        }\n//        int orientation = context.getResources().getConfiguration().orientation;\n//        int[] result;\n//        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {\n//            result = sLandscapeRealSizeCache;\n//            if (result == null) {\n//                result = doGetRealScreenSize(context);\n//                if(result[0] > result[1]){\n//                    // the result may be wrong sometimes, do not cache !!!!\n//                    sLandscapeRealSizeCache = result;\n//                }\n//            }\n//            return result;\n//        } else {\n//            result = sPortraitRealSizeCache;\n//            if (result == null) {\n//                result = doGetRealScreenSize(context);\n//                if(result[0] < result[1]){\n//                    // the result may be wrong sometimes, do not cache !!!!\n//                    sPortraitRealSizeCache = result;\n//                }\n//            }\n//            return result;\n//        }\n    }\n\n    private static int[] doGetRealScreenSize(Context context) {\n        int[] size = new int[2];\n        int widthPixels, heightPixels;\n        WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Display d = w.getDefaultDisplay();\n        DisplayMetrics metrics = new DisplayMetrics();\n        d.getMetrics(metrics);\n        // since SDK_INT = 1;\n        widthPixels = metrics.widthPixels;\n        heightPixels = metrics.heightPixels;\n        try {\n            // used when 17 > SDK_INT >= 14; includes window decorations (statusbar bar/menu bar)\n            widthPixels = (Integer) Display.class.getMethod(\"getRawWidth\").invoke(d);\n            heightPixels = (Integer) Display.class.getMethod(\"getRawHeight\").invoke(d);\n        } catch (Exception ignored) {\n        }\n        if (Build.VERSION.SDK_INT >= 17) {\n            try {\n                // used when SDK_INT >= 17; includes window decorations (statusbar bar/menu bar)\n                Point realSize = new Point();\n                d.getRealSize(realSize);\n\n\n                Display.class.getMethod(\"getRealSize\", Point.class).invoke(d, realSize);\n                widthPixels = realSize.x;\n                heightPixels = realSize.y;\n            } catch (Exception ignored) {\n            }\n        }\n\n        size[0] = widthPixels;\n        size[1] = heightPixels;\n        return size;\n    }\n\n    /**\n     * 剔除挖孔屏等导致的不可用区域后的 width\n     *\n     * @param activity\n     * @return\n     */\n    public static int getUsefulScreenWidth(Activity activity) {\n        return getUsefulScreenWidth(activity, QMUINotchHelper.hasNotch(activity));\n    }\n\n    public static int getUsefulScreenWidth(View view) {\n        return getUsefulScreenWidth(view.getContext(), QMUINotchHelper.hasNotch(view));\n    }\n\n    public static int getUsefulScreenWidth(Context context, boolean hasNotch) {\n        int result = getRealScreenSize(context)[0];\n        int orientation = context.getResources().getConfiguration().orientation;\n        boolean isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE;\n        if (!hasNotch) {\n            if (isLandscape && QMUIDeviceHelper.isEssentialPhone()\n                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n                // https://arstechnica.com/gadgets/2017/09/essential-phone-review-impressive-for-a-new-company-but-not-competitive/\n                // 这里说挖孔屏是状态栏高度的两倍， 但横屏好像小了一点点\n                result -= 2 * QMUIStatusBarHelper.getStatusbarHeight(context);\n            }\n            return result;\n        }\n        if (isLandscape) {\n            // 华为挖孔屏横屏时，会把整个 window 往后移动，因此，可用区域减小\n            if (QMUIDeviceHelper.isHuawei() && !QMUIDisplayHelper.huaweiIsNotchSetToShowInSetting(context)) {\n                result -= QMUINotchHelper.getNotchSizeInHuawei(context)[1];\n            }\n\n            // TODO vivo 设置-系统导航-导航手势样式-显示手势操作区域 打开的情况下，应该减去手势操作区域的高度，但无API\n            // TODO vivo 设置-显示与亮度-第三方应用显示比例 选为安全区域显示时，整个 window 会移动，应该减去移动区域，但无API\n            // TODO oppo 设置-显示与亮度-应用全屏显示-凹形区域显示控制 关闭是，整个 window 会移动，应该减去移动区域，但无API\n        }\n        return result;\n    }\n\n    /**\n     * 剔除挖孔屏等导致的不可用区域后的 height\n     *\n     * @param activity\n     * @return\n     */\n    public static int getUsefulScreenHeight(Activity activity) {\n        return getUsefulScreenHeight(activity, QMUINotchHelper.hasNotch(activity));\n    }\n\n    public static int getUsefulScreenHeight(View view) {\n        return getUsefulScreenHeight(view.getContext(), QMUINotchHelper.hasNotch(view));\n    }\n\n    private static int getUsefulScreenHeight(Context context, boolean hasNotch) {\n        int result = getRealScreenSize(context)[1];\n        int orientation = context.getResources().getConfiguration().orientation;\n        boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;\n        if (!hasNotch) {\n            if (isPortrait && QMUIDeviceHelper.isEssentialPhone()\n                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n                // https://arstechnica.com/gadgets/2017/09/essential-phone-review-impressive-for-a-new-company-but-not-competitive/\n                // 这里说挖孔屏是状态栏高度的两倍\n                result -= 2 * QMUIStatusBarHelper.getStatusbarHeight(context);\n            }\n            return result;\n        }\n//        if (isPortrait) {\n            // TODO vivo 设置-系统导航-导航手势样式-显示手势操作区域 打开的情况下，应该减去手势操作区域的高度，但无API\n            // TODO vivo 设置-显示与亮度-第三方应用显示比例 选为安全区域显示时，整个 window 会移动，应该减去移动区域，但无API\n            // TODO oppo 设置-显示与亮度-应用全屏显示-凹形区域显示控制 关闭是，整个 window 会移动，应该减去移动区域，但无API\n//        }\n        return result;\n    }\n\n    public static boolean isNavMenuExist(Context context) {\n        //通过判断设备是否有返回键、菜单键(不是虚拟键,是手机屏幕外的按键)来确定是否有navigation bar\n        boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();\n        boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);\n\n        if (!hasMenuKey && !hasBackKey) {\n            // 做任何你需要做的,这个设备有一个导航栏\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 单位转换: dp -> px\n     *\n     * @param dp\n     * @return\n     */\n    public static int dp2px(Context context, int dp) {\n        return (int) (getDensity(context) * dp + 0.5);\n    }\n\n    /**\n     * 单位转换: sp -> px\n     *\n     * @param sp\n     * @return\n     */\n    public static int sp2px(Context context, int sp) {\n        return (int) (getFontDensity(context) * sp + 0.5);\n    }\n\n    /**\n     * 单位转换:px -> dp\n     *\n     * @param px\n     * @return\n     */\n    public static int px2dp(Context context, int px) {\n        return (int) (px / getDensity(context) + 0.5);\n    }\n\n    /**\n     * 单位转换:px -> sp\n     *\n     * @param px\n     * @return\n     */\n    public static int px2sp(Context context, int px) {\n        return (int) (px / getFontDensity(context) + 0.5);\n    }\n\n    /**\n     * 判断是否有状态栏\n     *\n     * @param context\n     * @return\n     */\n    public static boolean hasStatusBar(Context context) {\n        if (context instanceof Activity) {\n            Activity activity = (Activity) context;\n            WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();\n            return (attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != WindowManager.LayoutParams.FLAG_FULLSCREEN;\n        }\n        return true;\n    }\n\n    /**\n     * 获取ActionBar高度\n     *\n     * @param context\n     * @return\n     */\n    public static int getActionBarHeight(Context context) {\n        int actionBarHeight = 0;\n        TypedValue tv = new TypedValue();\n        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {\n            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,\n                    context.getResources().getDisplayMetrics());\n        }\n        return actionBarHeight;\n    }\n\n    /**\n     * 获取状态栏高度\n     *\n     * @param context\n     * @return\n     */\n    public static int getStatusBarHeight(Context context) {\n        if(QMUIDeviceHelper.isXiaomi()){\n            int resourceId = context.getResources().getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n            if (resourceId > 0) {\n                return context.getResources().getDimensionPixelSize(resourceId);\n            }\n            return 0;\n        }\n        try {\n            Class<?> c = Class.forName(\"com.android.internal.R$dimen\");\n            Object obj = c.newInstance();\n            Field field = c.getField(\"status_bar_height\");\n            int x = Integer.parseInt(field.get(obj).toString());\n            if(x > 0){\n                return context.getResources().getDimensionPixelSize(x);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return 0;\n    }\n\n    /**\n     * 获取虚拟菜单的高度,若无则返回0\n     *\n     * @param context\n     * @return\n     */\n    public static int getNavMenuHeight(Context context) {\n        if (!isNavMenuExist(context)) {\n            return 0;\n        }\n        int resourceNavHeight = getResourceNavHeight(context);\n        if (resourceNavHeight >= 0) {\n            return resourceNavHeight;\n        }\n\n        // 小米 MIX 有nav bar, 而 getRealScreenSize(context)[1] - getScreenHeight(context) = 0\n        return getRealScreenSize(context)[1] - getScreenHeight(context);\n    }\n\n    private static int getResourceNavHeight(Context context){\n        // 小米4没有nav bar, 而 navigation_bar_height 有值\n        int resourceId = context.getResources().getIdentifier(\"navigation_bar_height\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            return context.getResources().getDimensionPixelSize(resourceId);\n        }\n        return -1;\n    }\n\n    public static final boolean hasCamera(Context context) {\n        if (sHasCamera == null) {\n            PackageManager pckMgr = context.getPackageManager();\n            boolean flag = pckMgr\n                    .hasSystemFeature(\"android.hardware.camera.front\");\n            boolean flag1 = pckMgr.hasSystemFeature(\"android.hardware.camera\");\n            boolean flag2;\n            flag2 = flag || flag1;\n            sHasCamera = flag2;\n        }\n        return sHasCamera;\n    }\n\n    /**\n     * 是否有硬件menu\n     *\n     * @param context\n     * @return\n     */\n    @SuppressWarnings(\"SimplifiableIfStatement\")\n    public static boolean hasHardwareMenuKey(Context context) {\n        boolean flag;\n        if (Build.VERSION.SDK_INT < 11)\n            flag = true;\n        else if (Build.VERSION.SDK_INT >= 14) {\n            flag = ViewConfiguration.get(context).hasPermanentMenuKey();\n        } else\n            flag = false;\n        return flag;\n    }\n\n    /**\n     * 是否有网络功能\n     *\n     * @param context\n     * @return\n     */\n    @SuppressLint(\"MissingPermission\")\n    public static boolean hasInternet(Context context) {\n        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n        return cm.getActiveNetworkInfo() != null;\n    }\n\n    /**\n     * 判断是否存在pckName包\n     *\n     * @param pckName\n     * @return\n     */\n    public static boolean isPackageExist(Context context, String pckName) {\n        try {\n            PackageInfo pckInfo = context.getPackageManager()\n                    .getPackageInfo(pckName, 0);\n            if (pckInfo != null)\n                return true;\n        } catch (PackageManager.NameNotFoundException ignored) {\n        }\n        return false;\n    }\n\n    /**\n     * 判断 SD Card 是否 ready\n     *\n     * @return\n     */\n    public static boolean isSdcardReady() {\n        return Environment.MEDIA_MOUNTED.equals(Environment\n                .getExternalStorageState());\n    }\n\n    /**\n     * 获取当前国家的语言\n     *\n     * @param context\n     * @return\n     */\n    public static String getCurCountryLan(Context context) {\n        Configuration config = context.getResources().getConfiguration();\n        Locale sysLocale;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            sysLocale = config.getLocales().get(0);\n        } else {\n            //noinspection deprecation\n            sysLocale = config.locale;\n        }\n        return sysLocale.getLanguage()\n                + \"-\"\n                + sysLocale.getCountry();\n    }\n\n    /**\n     * 判断是否为中文环境\n     *\n     * @param context\n     * @return\n     */\n    public static boolean isZhCN(Context context) {\n        Configuration config = context.getResources().getConfiguration();\n        Locale sysLocale;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            sysLocale = config.getLocales().get(0);\n        } else {\n            //noinspection deprecation\n            sysLocale = config.locale;\n        }\n        String lang = sysLocale.getCountry();\n        return lang.equalsIgnoreCase(\"CN\");\n    }\n\n    /**\n     * 设置全屏\n     *\n     * @param activity\n     */\n    public static void setFullScreen(Activity activity) {\n        Window window = activity.getWindow();\n        window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n\n    }\n\n    /**\n     * 取消全屏\n     *\n     * @param activity\n     */\n    public static void cancelFullScreen(Activity activity) {\n        Window window = activity.getWindow();\n        window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n    }\n\n    /**\n     * 判断是否全屏\n     *\n     * @param activity\n     * @return\n     */\n    public static boolean isFullScreen(Activity activity) {\n        WindowManager.LayoutParams params = activity.getWindow().getAttributes();\n        return (params.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN;\n    }\n\n\n    public static boolean isElevationSupported() {\n        return android.os.Build.VERSION.SDK_INT >= 21;\n    }\n\n    public static boolean hasNavigationBar(Context context) {\n        boolean hasNav = deviceHasNavigationBar();\n        if (!hasNav) {\n            return false;\n        }\n        if (QMUIDeviceHelper.isVivo()) {\n            return vivoNavigationGestureEnabled(context);\n        }\n        return true;\n    }\n\n    /**\n     * 判断设备是否存在NavigationBar\n     *\n     * @return true 存在, false 不存在\n     */\n    private static boolean deviceHasNavigationBar() {\n        boolean haveNav = false;\n        try {\n            //1.通过WindowManagerGlobal获取windowManagerService\n            // 反射方法：IWindowManager windowManagerService = WindowManagerGlobal.getWindowManagerService();\n            Class<?> windowManagerGlobalClass = Class.forName(\"android.view.WindowManagerGlobal\");\n            Method getWmServiceMethod = windowManagerGlobalClass.getDeclaredMethod(\"getWindowManagerService\");\n            getWmServiceMethod.setAccessible(true);\n            //getWindowManagerService是静态方法，所以invoke null\n            Object iWindowManager = getWmServiceMethod.invoke(null);\n\n            //2.获取windowMangerService的hasNavigationBar方法返回值\n            // 反射方法：haveNav = windowManagerService.hasNavigationBar();\n            Class<?> iWindowManagerClass = iWindowManager.getClass();\n            Method hasNavBarMethod = iWindowManagerClass.getDeclaredMethod(\"hasNavigationBar\");\n            hasNavBarMethod.setAccessible(true);\n            haveNav = (Boolean) hasNavBarMethod.invoke(iWindowManager);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return haveNav;\n    }\n\n    // ====================== Setting ===========================\n    private static final String VIVO_NAVIGATION_GESTURE = \"navigation_gesture_on\";\n    private static final String HUAWAI_DISPLAY_NOTCH_STATUS = \"display_notch_status\";\n    private static final String XIAOMI_DISPLAY_NOTCH_STATUS = \"force_black\";\n    private static final String XIAOMI_FULLSCREEN_GESTURE = \"force_fsg_nav_bar\";\n\n    /**\n     * 获取vivo手机设置中的\"navigation_gesture_on\"值，判断当前系统是使用导航键还是手势导航操作\n     *\n     * @param context app Context\n     * @return false 表示使用的是虚拟导航键(NavigationBar)， true 表示使用的是手势， 默认是false\n     */\n    public static boolean vivoNavigationGestureEnabled(Context context) {\n        int val = Settings.Secure.getInt(context.getContentResolver(), VIVO_NAVIGATION_GESTURE, 0);\n        return val != 0;\n    }\n\n\n    public static boolean xiaomiNavigationGestureEnabled(Context context) {\n        int val = Settings.Global.getInt(context.getContentResolver(), XIAOMI_FULLSCREEN_GESTURE, 0);\n        return val != 0;\n    }\n\n\n    public static boolean huaweiIsNotchSetToShowInSetting(Context context) {\n        // 0: 默认\n        // 1: 隐藏显示区域\n        int result = Settings.Secure.getInt(context.getContentResolver(), HUAWAI_DISPLAY_NOTCH_STATUS, 0);\n        return result == 0;\n    }\n\n    @TargetApi(17)\n    public static boolean xiaomiIsNotchSetToShowInSetting(Context context) {\n        return Settings.Global.getInt(context.getContentResolver(), XIAOMI_DISPLAY_NOTCH_STATUS, 0) == 0;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIDrawableHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.LightingColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.GradientDrawable;\nimport android.graphics.drawable.LayerDrawable;\nimport android.graphics.drawable.ShapeDrawable;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.FloatRange;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.core.graphics.drawable.DrawableCompat;\n\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.QMUILog;\n\n/**\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUIDrawableHelper {\n    private static final String TAG = QMUIDrawableHelper.class.getSimpleName();\n\n    //节省每次创建时产生的开销，但要注意多线程操作synchronized\n    private static final Canvas sCanvas = new Canvas();\n\n    /**\n     * 从一个view创建Bitmap。\n     * 注意点：绘制之前要清掉 View 的焦点，因为焦点可能会改变一个 View 的 UI 状态。\n     * 来源：https://github.com/tyrantgit/ExplosionField\n     *\n     * @param view  传入一个 View，会获取这个 View 的内容创建 Bitmap。\n     * @param scale 缩放比例，对创建的 Bitmap 进行缩放，数值支持从 0 到 1。\n     */\n    public static Bitmap createBitmapFromView(View view, float scale) {\n        if (view instanceof ImageView) {\n            Drawable drawable = ((ImageView) view).getDrawable();\n            if (drawable != null && drawable instanceof BitmapDrawable) {\n                return ((BitmapDrawable) drawable).getBitmap();\n            }\n        }\n        view.clearFocus();\n        Bitmap bitmap = createBitmapSafely((int) (view.getWidth() * scale),\n                (int) (view.getHeight() * scale), Bitmap.Config.ARGB_8888, 1);\n        if (bitmap != null) {\n            synchronized (sCanvas) {\n                Canvas canvas = sCanvas;\n                canvas.setBitmap(bitmap);\n                canvas.save();\n                canvas.drawColor(Color.WHITE); // 防止 View 上面有些区域空白导致最终 Bitmap 上有些区域变黑\n                canvas.scale(scale, scale);\n                view.draw(canvas);\n                canvas.restore();\n                canvas.setBitmap(null);\n            }\n        }\n        return bitmap;\n    }\n\n    public static Bitmap createBitmapFromView(View view) {\n        return createBitmapFromView(view, 1f);\n    }\n\n    /**\n     * 从一个view创建Bitmap。把view的区域截掉leftCrop/topCrop/rightCrop/bottomCrop\n     */\n    public static Bitmap createBitmapFromView(View view, int leftCrop, int topCrop, int rightCrop, int bottomCrop) {\n        Bitmap originBitmap = QMUIDrawableHelper.createBitmapFromView(view);\n        if (originBitmap == null) {\n            return null;\n        }\n        Bitmap cutBitmap = createBitmapSafely(view.getWidth() - rightCrop - leftCrop, view.getHeight() - topCrop - bottomCrop, Bitmap.Config.ARGB_8888, 1);\n        if (cutBitmap == null) {\n            return null;\n        }\n        Canvas canvas = new Canvas(cutBitmap);\n        Rect src = new Rect(leftCrop, topCrop, view.getWidth() - rightCrop, view.getHeight() - bottomCrop);\n        Rect dest = new Rect(0, 0, view.getWidth() - rightCrop - leftCrop, view.getHeight() - topCrop - bottomCrop);\n        canvas.drawColor(Color.WHITE); // 防止 View 上面有些区域空白导致最终 Bitmap 上有些区域变黑\n        canvas.drawBitmap(originBitmap, src, dest, null);\n        originBitmap.recycle();\n        return cutBitmap;\n    }\n\n    /**\n     * 安全的创建bitmap。\n     * 如果新建 Bitmap 时产生了 OOM，可以主动进行一次 GC - System.gc()，然后再次尝试创建。\n     *\n     * @param width      Bitmap 宽度。\n     * @param height     Bitmap 高度。\n     * @param config     传入一个 Bitmap.Config。\n     * @param retryCount 创建 Bitmap 时产生 OOM 后，主动重试的次数。\n     * @return 返回创建的 Bitmap。\n     */\n    public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {\n        try {\n            return Bitmap.createBitmap(width, height, config);\n        } catch (OutOfMemoryError e) {\n            e.printStackTrace();\n            if (retryCount > 0) {\n                System.gc();\n                return createBitmapSafely(width, height, config, retryCount - 1);\n            }\n            return null;\n        }\n    }\n\n    /**\n     * 创建一张指定大小的纯色图片，支持圆角\n     *\n     * @param resources    Resources对象，用于创建BitmapDrawable\n     * @param width        图片的宽度\n     * @param height       图片的高度\n     * @param cornerRadius 图片的圆角，不需要则传0\n     * @param filledColor  图片的填充色\n     * @return 指定大小的纯色图片\n     */\n    public static BitmapDrawable createDrawableWithSize(Resources resources, int width, int height, int cornerRadius, @ColorInt int filledColor) {\n        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);\n        Canvas canvas = new Canvas(output);\n\n        if (filledColor == 0) {\n            filledColor = Color.TRANSPARENT;\n        }\n\n        if (cornerRadius > 0) {\n            Paint paint = new Paint();\n            paint.setAntiAlias(true);\n            paint.setStyle(Paint.Style.FILL);\n            paint.setColor(filledColor);\n            canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);\n        } else {\n            canvas.drawColor(filledColor);\n        }\n        return new BitmapDrawable(resources, output);\n    }\n\n    /**\n     * 设置Drawable的颜色\n     * <b>这里不对Drawable进行mutate()，会影响到所有用到这个Drawable的地方，如果要避免，请先自行mutate()</b>\n     *\n     * please use {@link DrawableCompat#setTint(Drawable, int)} replace this.\n     */\n    @Deprecated\n    public static ColorFilter setDrawableTintColor(Drawable drawable, @ColorInt int tintColor) {\n        LightingColorFilter colorFilter = new LightingColorFilter(Color.argb(255, 0, 0, 0), tintColor);\n        if(drawable != null){\n            drawable.setColorFilter(colorFilter);\n        }\n        return colorFilter;\n    }\n\n    /**\n     * 由一个drawable生成bitmap\n     */\n    public static Bitmap drawableToBitmap(Drawable drawable) {\n        if (drawable == null)\n            return null;\n        else if (drawable instanceof BitmapDrawable) {\n            return ((BitmapDrawable) drawable).getBitmap();\n        }\n\n        int intrinsicWidth = drawable.getIntrinsicWidth();\n        int intrinsicHeight = drawable.getIntrinsicHeight();\n\n        if (!(intrinsicWidth > 0 && intrinsicHeight > 0))\n            return null;\n\n        try {\n            Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888\n                    : Bitmap.Config.RGB_565;\n            Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, config);\n            Canvas canvas = new Canvas(bitmap);\n            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n            drawable.draw(canvas);\n            return bitmap;\n        } catch (OutOfMemoryError e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * 创建一张渐变图片，支持韵脚。\n     *\n     * @param startColor 渐变开始色\n     * @param endColor   渐变结束色\n     * @param radius     圆角大小\n     * @param centerX    渐变中心点 X 轴坐标\n     * @param centerY    渐变中心点 Y 轴坐标\n     * @return 返回所创建的渐变图片。\n     */\n    @TargetApi(16)\n    public static GradientDrawable createCircleGradientDrawable(@ColorInt int startColor,\n                                                                @ColorInt int endColor, int radius,\n                                                                @FloatRange(from = 0f, to = 1f) float centerX,\n                                                                @FloatRange(from = 0f, to = 1f) float centerY) {\n        GradientDrawable gradientDrawable = new GradientDrawable();\n        gradientDrawable.setColors(new int[]{\n                startColor,\n                endColor\n        });\n        gradientDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);\n        gradientDrawable.setGradientRadius(radius);\n        gradientDrawable.setGradientCenter(centerX, centerY);\n        return gradientDrawable;\n    }\n\n\n    /**\n     * 动态创建带上分隔线或下分隔线的Drawable。\n     *\n     * @param separatorColor 分割线颜色。\n     * @param bgColor        Drawable 的背景色。\n     * @param top            true 则分割线为上分割线，false 则为下分割线。\n     * @return 返回所创建的 Drawable。\n     */\n    public static LayerDrawable createItemSeparatorBg(@ColorInt int separatorColor, @ColorInt int bgColor, int separatorHeight, boolean top) {\n\n        ShapeDrawable separator = new ShapeDrawable();\n        separator.getPaint().setStyle(Paint.Style.FILL);\n        separator.getPaint().setColor(separatorColor);\n\n        ShapeDrawable bg = new ShapeDrawable();\n        bg.getPaint().setStyle(Paint.Style.FILL);\n        bg.getPaint().setColor(bgColor);\n\n        Drawable[] layers = {separator, bg};\n        LayerDrawable layerDrawable = new LayerDrawable(layers);\n\n        layerDrawable.setLayerInset(1, 0, top ? separatorHeight : 0, 0, top ? 0 : separatorHeight);\n        return layerDrawable;\n    }\n\n\n    /////////////// VectorDrawable /////////////////////\n\n    public static\n    @Nullable\n    Drawable getVectorDrawable(Context context, @DrawableRes int resVector) {\n        try {\n            return AppCompatResources.getDrawable(context, resVector);\n        } catch (Exception e) {\n            QMUILog.d(TAG, \"Error in getVectorDrawable. resVector=\" + resVector + \", resName=\" + context.getResources().getResourceName(resVector) + e.getMessage());\n            return null;\n        }\n    }\n\n    public static Bitmap vectorDrawableToBitmap(Context context, @DrawableRes int resVector) {\n        Drawable drawable = getVectorDrawable(context, resVector);\n        if (drawable != null) {\n            Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);\n            Canvas c = new Canvas(b);\n            drawable.setBounds(0, 0, c.getWidth(), c.getHeight());\n            drawable.draw(c);\n            return b;\n        }\n        return null;\n    }\n\n    /////////////// VectorDrawable /////////////////////\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIKeyboardHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2016-11-07\n * <p>\n * https://github.com/yshrsmz/KeyboardVisibilityEvent/blob/master/keyboardvisibilityevent/src/main/java/net/yslibrary/android/keyboardvisibilityevent/KeyboardVisibilityEvent.java\n */\n\npublic class QMUIKeyboardHelper {\n    /**\n     * 显示软键盘的延迟时间\n     */\n    public static final int SHOW_KEYBOARD_DELAY_TIME = 200;\n    private static final String TAG = \"QMUIKeyboardHelper\";\n    public final static int KEYBOARD_VISIBLE_THRESHOLD_DP = 100;\n\n\n    public static void showKeyboard(final EditText editText, boolean delay) {\n        showKeyboard(editText, delay ? SHOW_KEYBOARD_DELAY_TIME : 0);\n    }\n\n\n    /**\n     * 针对给定的editText显示软键盘（editText会先获得焦点）. 可以和{@link #hideKeyboard(View)}\n     * 搭配使用，进行键盘的显示隐藏控制。\n     */\n\n    public static void showKeyboard(final EditText editText, int delay) {\n        if (null == editText)\n            return;\n\n        if (!editText.requestFocus()) {\n            Log.w(TAG, \"showSoftInput() can not get focus\");\n            return;\n        }\n        if (delay > 0) {\n            editText.postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    InputMethodManager imm = (InputMethodManager) editText.getContext().getApplicationContext()\n                            .getSystemService(Context.INPUT_METHOD_SERVICE);\n                    imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);\n                }\n            }, delay);\n        } else {\n            InputMethodManager imm = (InputMethodManager) editText.getContext().getApplicationContext()\n                    .getSystemService(Context.INPUT_METHOD_SERVICE);\n            imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);\n        }\n    }\n\n    /**\n     * 隐藏软键盘 可以和{@link #showKeyboard(EditText, boolean)}搭配使用，进行键盘的显示隐藏控制。\n     *\n     * @param view 当前页面上任意一个可用的view\n     */\n    public static boolean hideKeyboard(final View view) {\n        if (null == view)\n            return false;\n\n        InputMethodManager inputManager = (InputMethodManager) view.getContext().getApplicationContext()\n                .getSystemService(Context.INPUT_METHOD_SERVICE);\n        // 即使当前焦点不在editText，也是可以隐藏的。\n        return inputManager.hideSoftInputFromWindow(view.getWindowToken(),\n                InputMethodManager.HIDE_NOT_ALWAYS);\n    }\n\n\n    public static void listenKeyBoardWithOffsetSelf(final View view, final boolean minusNav){\n        ViewCompat.setWindowInsetsAnimationCallback(view, new WindowInsetsAnimationCompat.Callback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) {\n            @NonNull\n            @Override\n            public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {\n                int height;\n                Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());\n                height = ime.bottom;\n                if(minusNav){\n                    Insets nav = insets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars());\n                    height -= nav.bottom;\n                }\n                QMUIViewHelper.getOrCreateOffsetHelper(view).setTopAndBottomOffset(-height);\n                return insets;\n            }\n        });\n    }\n\n    public static void listenKeyBoardWithOffsetSelfHalf(final View view, final boolean minusNav){\n        ViewCompat.setWindowInsetsAnimationCallback(view, new WindowInsetsAnimationCompat.Callback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) {\n            @NonNull\n            @Override\n            public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {\n                int height;\n                Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());\n                height = ime.bottom;\n                if(minusNav){\n                    Insets nav = insets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars());\n                    height -= nav.bottom;\n                }\n                QMUIViewHelper.getOrCreateOffsetHelper(view).setTopAndBottomOffset(-height / 2);\n                return insets;\n            }\n        });\n    }\n\n    /**\n     * Set keyboard visibility change event listener.\n     *\n     * @param activity Activity\n     * @param listener KeyboardVisibilityEventListener\n     */\n    @SuppressWarnings(\"deprecation\")\n    public static void setVisibilityEventListener(final Activity activity,\n                                                  final KeyboardVisibilityEventListener listener) {\n\n        if (activity == null) {\n            throw new NullPointerException(\"Parameter:activity must not be null\");\n        }\n\n        if (listener == null) {\n            throw new NullPointerException(\"Parameter:listener must not be null\");\n        }\n\n        final View activityRoot = QMUIViewHelper.getActivityRoot(activity);\n\n        final ViewTreeObserver.OnGlobalLayoutListener layoutListener =\n                new ViewTreeObserver.OnGlobalLayoutListener() {\n\n                    private final Rect r = new Rect();\n\n                    private final int visibleThreshold = Math.round(\n                            QMUIDisplayHelper.dp2px(activity, KEYBOARD_VISIBLE_THRESHOLD_DP));\n\n                    private boolean wasOpened = false;\n\n                    @Override\n                    public void onGlobalLayout() {\n                        activityRoot.getWindowVisibleDisplayFrame(r);\n\n                        int heightDiff = activityRoot.getRootView().getHeight() - r.height();\n\n                        boolean isOpen = heightDiff > visibleThreshold;\n\n                        if (isOpen == wasOpened) {\n                            // keyboard state has not changed\n                            return;\n                        }\n\n                        wasOpened = isOpen;\n\n                        boolean removeListener = listener.onVisibilityChanged(isOpen, heightDiff);\n                        if (removeListener) {\n                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                                activityRoot.getViewTreeObserver()\n                                        .removeOnGlobalLayoutListener(this);\n                            } else {\n                                activityRoot.getViewTreeObserver()\n                                        .removeGlobalOnLayoutListener(this);\n                            }\n                        }\n                    }\n                };\n        activityRoot.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);\n        activity.getApplication()\n                .registerActivityLifecycleCallbacks(new QMUIActivityLifecycleCallbacks(activity) {\n                    @Override\n                    protected void onTargetActivityDestroyed() {\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                            activityRoot.getViewTreeObserver()\n                                    .removeOnGlobalLayoutListener(layoutListener);\n                        } else {\n                            activityRoot.getViewTreeObserver()\n                                    .removeGlobalOnLayoutListener(layoutListener);\n                        }\n                    }\n                });\n    }\n\n    /**\n     * Determine if keyboard is visible\n     *\n     * @param activity Activity\n     * @return Whether keyboard is visible or not\n     */\n    public static boolean isKeyboardVisible(Activity activity) {\n        Rect r = new Rect();\n\n        View activityRoot = QMUIViewHelper.getActivityRoot(activity);\n        int visibleThreshold =\n                Math.round(QMUIDisplayHelper.dp2px(activity, KEYBOARD_VISIBLE_THRESHOLD_DP));\n\n        activityRoot.getWindowVisibleDisplayFrame(r);\n\n        int heightDiff = activityRoot.getRootView().getHeight() - r.height();\n\n        return heightDiff > visibleThreshold;\n    }\n\n\n    public interface KeyboardVisibilityEventListener {\n\n        /**\n         * @return to remove global listener or not\n         */\n        boolean onVisibilityChanged(boolean isOpen, int heightDiff);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUILangHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport androidx.annotation.Nullable;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.Locale;\nimport java.util.Objects;\n\n/**\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUILangHelper {\n\n    /**\n     * 获取数值的位数，例如9返回1，99返回2，999返回3\n     *\n     * @param number 要计算位数的数值，必须>0\n     * @return 数值的位数，若传的参数小于等于0，则返回0\n     */\n    public static int getNumberDigits(int number) {\n        if (number <= 0) return 0;\n        return (int) (Math.log10(number) + 1);\n    }\n\n\n    public static int getNumberDigits(long number) {\n        if (number <= 0) return 0;\n        return (int) (Math.log10(number) + 1);\n    }\n\n\n    public static String formatNumberToLimitedDigits(int number, int maxDigits) {\n        if (getNumberDigits(number) > maxDigits) {\n            StringBuilder result = new StringBuilder();\n            for (int digit = 1; digit <= maxDigits; digit++) {\n                result.append(\"9\");\n            }\n            result.append(\"+\");\n            return result.toString();\n        } else {\n            return String.valueOf(number);\n        }\n    }\n\n    /**\n     * 规范化价格字符串显示的工具类\n     *\n     * @param price 价格\n     * @return 保留两位小数的价格字符串\n     */\n    public static String regularizePrice(float price) {\n        return String.format(Locale.CHINESE, \"%.2f\", price);\n    }\n\n    /**\n     * 规范化价格字符串显示的工具类\n     *\n     * @param price 价格\n     * @return 保留两位小数的价格字符串\n     */\n    public static String regularizePrice(double price) {\n        return String.format(Locale.CHINESE, \"%.2f\", price);\n    }\n\n\n    public static boolean isNullOrEmpty(@Nullable CharSequence string) {\n        return string == null || string.length() == 0;\n    }\n\n    public static void close(Closeable c) {\n        if (c != null) {\n            try {\n                c.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Deprecated\n    public static boolean objectEquals(Object a, Object b) {\n        return Objects.equals(a, b);\n    }\n\n    public static int constrain(int amount, int low, int high) {\n        return amount < low ? low : (amount > high ? high : amount);\n    }\n\n    public static float constrain(float amount, float low, float high) {\n        return amount < low ? low : (amount > high ? high : amount);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUINotchHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.util.Log;\nimport android.view.Display;\nimport android.view.DisplayCutout;\nimport android.view.Surface;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowInsets;\nimport android.view.WindowManager;\n\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.lang.reflect.Method;\n\npublic class QMUINotchHelper {\n\n    private static final String TAG = \"QMUINotchHelper\";\n\n    private static final int NOTCH_IN_SCREEN_VOIO = 0x00000020;\n    private static final String MIUI_NOTCH = \"ro.miui.notch\";\n    private static Boolean sHasNotch = null;\n    private static Rect sRotation0SafeInset = null;\n    private static Rect sRotation90SafeInset = null;\n    private static Rect sRotation180SafeInset = null;\n    private static Rect sRotation270SafeInset = null;\n    private static int[] sNotchSizeInHawei = null;\n    private static Boolean sHuaweiIsNotchSetToShow = null;\n\n    public static boolean hasNotchInVivo(Context context) {\n        boolean ret = false;\n        try {\n            ClassLoader cl = context.getClassLoader();\n            Class ftFeature = cl.loadClass(\"android.util.FtFeature\");\n            Method[] methods = ftFeature.getDeclaredMethods();\n            if (methods != null) {\n                for (int i = 0; i < methods.length; i++) {\n                    Method method = methods[i];\n                    if (method.getName().equalsIgnoreCase(\"isFeatureSupport\")) {\n                        ret = (boolean) method.invoke(ftFeature, NOTCH_IN_SCREEN_VOIO);\n                        break;\n                    }\n                }\n            }\n        } catch (ClassNotFoundException e) {\n            Log.i(TAG, \"hasNotchInVivo ClassNotFoundException\");\n        } catch (Exception e) {\n            Log.e(TAG, \"hasNotchInVivo Exception\");\n        }\n        return ret;\n    }\n\n\n    public static boolean hasNotchInHuawei(Context context) {\n        boolean hasNotch = false;\n        try {\n            ClassLoader cl = context.getClassLoader();\n            Class HwNotchSizeUtil = cl.loadClass(\"com.huawei.android.util.HwNotchSizeUtil\");\n            Method get = HwNotchSizeUtil.getMethod(\"hasNotchInScreen\");\n            hasNotch = (boolean) get.invoke(HwNotchSizeUtil);\n        } catch (ClassNotFoundException e) {\n            Log.i(TAG, \"hasNotchInHuawei ClassNotFoundException\");\n        } catch (NoSuchMethodException e) {\n            Log.e(TAG, \"hasNotchInHuawei NoSuchMethodException\");\n        } catch (Exception e) {\n            Log.e(TAG, \"hasNotchInHuawei Exception\");\n        }\n        return hasNotch;\n    }\n\n    public static boolean hasNotchInOppo(Context context) {\n        return context.getPackageManager()\n                .hasSystemFeature(\"com.oppo.feature.screen.heteromorphism\");\n    }\n\n    @SuppressLint(\"PrivateApi\")\n    public static boolean hasNotchInXiaomi(Context context) {\n        try {\n            Class spClass = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = spClass.getDeclaredMethod(\"getInt\", String.class, int.class);\n            getMethod.setAccessible(true);\n            int hasNotch = (int) getMethod.invoke(null, MIUI_NOTCH, 0);\n            return hasNotch == 1;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    public static boolean hasNotch(View view){\n        if (sHasNotch == null) {\n            if(isNotchOfficialSupport()){\n                if(!attachHasOfficialNotch(view)){\n                    return false;\n                }\n            }else {\n                sHasNotch = has3rdNotch(view.getContext());\n            }\n        }\n        return sHasNotch;\n    }\n\n\n    public static boolean hasNotch(Activity activity) {\n        if (sHasNotch == null) {\n            if(isNotchOfficialSupport()){\n                Window window = activity.getWindow();\n                if(window == null){\n                    return false;\n                }\n                View decorView = window.getDecorView();\n                if(decorView == null){\n                    return false;\n                }\n                if(!attachHasOfficialNotch(decorView)){\n                    return false;\n                }\n            }else {\n                sHasNotch = has3rdNotch(activity);\n            }\n        }\n        return sHasNotch;\n    }\n\n    /**\n     *\n     * @param view\n     * @return false indicates the failure to get the result\n     */\n    @TargetApi(28)\n    private static boolean attachHasOfficialNotch(View view){\n        WindowInsets windowInsets = view.getRootWindowInsets();\n        if(windowInsets != null){\n            DisplayCutout displayCutout = windowInsets.getDisplayCutout();\n            sHasNotch = displayCutout != null;\n            return true;\n        }else{\n            // view not attached, do nothing\n            return false;\n        }\n    }\n\n    public static boolean has3rdNotch(Context context){\n        if (QMUIDeviceHelper.isHuawei()) {\n            return hasNotchInHuawei(context);\n        } else if (QMUIDeviceHelper.isVivo()) {\n            return hasNotchInVivo(context);\n        } else if (QMUIDeviceHelper.isOppo()) {\n            return hasNotchInOppo(context);\n        } else if (QMUIDeviceHelper.isXiaomi()) {\n            return hasNotchInXiaomi(context);\n        }\n        return false;\n    }\n\n    public static int getSafeInsetTop(Activity activity) {\n        if (!hasNotch(activity)) {\n            return 0;\n        }\n        return getSafeInsetRect(activity).top;\n    }\n\n    public static int getSafeInsetBottom(Activity activity) {\n        if (!hasNotch(activity)) {\n            return 0;\n        }\n        return getSafeInsetRect(activity).bottom;\n    }\n\n    public static int getSafeInsetLeft(Activity activity) {\n        if (!hasNotch(activity)) {\n            return 0;\n        }\n        return getSafeInsetRect(activity).left;\n    }\n\n    public static int getSafeInsetRight(Activity activity) {\n        if (!hasNotch(activity)) {\n            return 0;\n        }\n        return getSafeInsetRect(activity).right;\n    }\n\n\n    public static int getSafeInsetTop(View view) {\n        if (!hasNotch(view)) {\n            return 0;\n        }\n        return getSafeInsetRect(view).top;\n    }\n\n    public static int getSafeInsetBottom(View view) {\n        if (!hasNotch(view)) {\n            return 0;\n        }\n        return getSafeInsetRect(view).bottom;\n    }\n\n    public static int getSafeInsetLeft(View view) {\n        if (!hasNotch(view)) {\n            return 0;\n        }\n        return getSafeInsetRect(view).left;\n    }\n\n    public static int getSafeInsetRight(View view) {\n        if (!hasNotch(view)) {\n            return 0;\n        }\n        return getSafeInsetRect(view).right;\n    }\n\n\n    private static void clearAllRectInfo() {\n        sRotation0SafeInset = null;\n        sRotation90SafeInset = null;\n        sRotation180SafeInset = null;\n        sRotation270SafeInset = null;\n    }\n\n    private static void clearPortraitRectInfo() {\n        sRotation0SafeInset = null;\n        sRotation180SafeInset = null;\n    }\n\n    private static void clearLandscapeRectInfo() {\n        sRotation90SafeInset = null;\n        sRotation270SafeInset = null;\n    }\n\n    private static Rect getSafeInsetRect(Activity activity) {\n        if(isNotchOfficialSupport()){\n            Rect rect = new Rect();\n            View decorView = activity.getWindow().getDecorView();\n            getOfficialSafeInsetRect(decorView, rect);\n            return rect;\n        }\n        return get3rdSafeInsetRect(activity);\n    }\n\n    private static Rect getSafeInsetRect(View view) {\n        if(isNotchOfficialSupport()){\n            Rect rect = new Rect();\n            getOfficialSafeInsetRect(view, rect);\n            return rect;\n        }\n        return get3rdSafeInsetRect(view.getContext());\n    }\n\n    @TargetApi(28)\n    private static void getOfficialSafeInsetRect(View view, Rect out) {\n        if(view == null){\n            return;\n        }\n        WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);\n        if(rootWindowInsets == null){\n            return;\n        }\n        Insets cutoutInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout());\n        out.set(cutoutInsets.left, cutoutInsets.top, cutoutInsets.right, cutoutInsets.bottom);\n    }\n\n    private static Rect get3rdSafeInsetRect(Context context){\n        // 全面屏设置项更改\n        if (QMUIDeviceHelper.isHuawei()) {\n            boolean isHuaweiNotchSetToShow = QMUIDisplayHelper.huaweiIsNotchSetToShowInSetting(context);\n            if (sHuaweiIsNotchSetToShow != null && sHuaweiIsNotchSetToShow != isHuaweiNotchSetToShow) {\n                clearLandscapeRectInfo();\n            }\n            sHuaweiIsNotchSetToShow = isHuaweiNotchSetToShow;\n        }\n        int screenRotation = getScreenRotation(context);\n        if (screenRotation == Surface.ROTATION_90) {\n            if (sRotation90SafeInset == null) {\n                sRotation90SafeInset = getRectInfoRotation90(context);\n            }\n            return sRotation90SafeInset;\n        } else if (screenRotation == Surface.ROTATION_180) {\n            if (sRotation180SafeInset == null) {\n                sRotation180SafeInset = getRectInfoRotation180(context);\n            }\n            return sRotation180SafeInset;\n        } else if (screenRotation == Surface.ROTATION_270) {\n            if (sRotation270SafeInset == null) {\n                sRotation270SafeInset = getRectInfoRotation270(context);\n            }\n            return sRotation270SafeInset;\n        } else {\n            if (sRotation0SafeInset == null) {\n                sRotation0SafeInset = getRectInfoRotation0(context);\n            }\n            return sRotation0SafeInset;\n        }\n    }\n\n    private static Rect getRectInfoRotation0(Context context) {\n        Rect rect = new Rect();\n        if (QMUIDeviceHelper.isVivo()) {\n            // TODO vivo 显示与亮度-第三方应用显示比例\n            rect.top = getNotchHeightInVivo(context);\n            rect.bottom = 0;\n        } else if (QMUIDeviceHelper.isOppo()) {\n            // TODO OPPO 设置-显示-应用全屏显示-凹形区域显示控制\n            rect.top = QMUIStatusBarHelper.getStatusbarHeight(context);\n            rect.bottom = 0;\n        } else if (QMUIDeviceHelper.isHuawei()) {\n            int[] notchSize = getNotchSizeInHuawei(context);\n            rect.top = notchSize[1];\n            rect.bottom = 0;\n        } else if (QMUIDeviceHelper.isXiaomi()) {\n            rect.top = getNotchHeightInXiaomi(context);\n            rect.bottom = 0;\n        }\n        return rect;\n    }\n\n    private static Rect getRectInfoRotation90(Context context) {\n        Rect rect = new Rect();\n        if (QMUIDeviceHelper.isVivo()) {\n            rect.left = getNotchHeightInVivo(context);\n            rect.right = 0;\n        } else if (QMUIDeviceHelper.isOppo()) {\n            rect.left = QMUIStatusBarHelper.getStatusbarHeight(context);\n            rect.right = 0;\n        } else if (QMUIDeviceHelper.isHuawei()) {\n            if (sHuaweiIsNotchSetToShow) {\n                rect.left = getNotchSizeInHuawei(context)[1];\n            } else {\n                rect.left = 0;\n            }\n            rect.right = 0;\n        } else if (QMUIDeviceHelper.isXiaomi()) {\n            rect.left = getNotchHeightInXiaomi(context);\n            rect.right = 0;\n        }\n        return rect;\n    }\n\n    private static Rect getRectInfoRotation180(Context context) {\n        Rect rect = new Rect();\n        if (QMUIDeviceHelper.isVivo()) {\n            rect.top = 0;\n            rect.bottom = getNotchHeightInVivo(context);\n        } else if (QMUIDeviceHelper.isOppo()) {\n            rect.top = 0;\n            rect.bottom = QMUIStatusBarHelper.getStatusbarHeight(context);\n        } else if (QMUIDeviceHelper.isHuawei()) {\n            int[] notchSize = getNotchSizeInHuawei(context);\n            rect.top = 0;\n            rect.bottom = notchSize[1];\n        } else if (QMUIDeviceHelper.isXiaomi()) {\n            rect.top = 0;\n            rect.bottom = getNotchHeightInXiaomi(context);\n        }\n        return rect;\n    }\n\n    private static Rect getRectInfoRotation270(Context context) {\n        Rect rect = new Rect();\n        if (QMUIDeviceHelper.isVivo()) {\n            rect.right = getNotchHeightInVivo(context);\n            rect.left = 0;\n        } else if (QMUIDeviceHelper.isOppo()) {\n            rect.right = QMUIStatusBarHelper.getStatusbarHeight(context);\n            rect.left = 0;\n        } else if (QMUIDeviceHelper.isHuawei()) {\n            if (sHuaweiIsNotchSetToShow) {\n                rect.right = getNotchSizeInHuawei(context)[1];\n            } else {\n                rect.right = 0;\n            }\n            rect.left = 0;\n        } else if (QMUIDeviceHelper.isXiaomi()) {\n            rect.right = getNotchHeightInXiaomi(context);\n            rect.left = 0;\n        }\n        return rect;\n    }\n\n\n    public static int[] getNotchSizeInHuawei(Context context) {\n        if (sNotchSizeInHawei == null) {\n            sNotchSizeInHawei = new int[]{0, 0};\n            try {\n                ClassLoader cl = context.getClassLoader();\n                Class HwNotchSizeUtil = cl.loadClass(\"com.huawei.android.util.HwNotchSizeUtil\");\n                Method get = HwNotchSizeUtil.getMethod(\"getNotchSize\");\n                sNotchSizeInHawei = (int[]) get.invoke(HwNotchSizeUtil);\n            } catch (ClassNotFoundException e) {\n                Log.e(TAG, \"getNotchSizeInHuawei ClassNotFoundException\");\n            } catch (NoSuchMethodException e) {\n                Log.e(TAG, \"getNotchSizeInHuawei NoSuchMethodException\");\n            } catch (Exception e) {\n                Log.e(TAG, \"getNotchSizeInHuawei Exception\");\n            }\n\n        }\n        return sNotchSizeInHawei;\n    }\n\n    public static int getNotchWidthInXiaomi(Context context) {\n        int resourceId = context.getResources().getIdentifier(\"notch_width\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            return context.getResources().getDimensionPixelSize(resourceId);\n        }\n        return -1;\n    }\n\n    public static int getNotchHeightInXiaomi(Context context) {\n        int resourceId = context.getResources().getIdentifier(\"notch_height\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            return context.getResources().getDimensionPixelSize(resourceId);\n        }\n        return QMUIDisplayHelper.getStatusBarHeight(context);\n    }\n\n    public static int getNotchWidthInVivo(Context context){\n        return QMUIDisplayHelper.dp2px(context, 100);\n    }\n\n    public static int getNotchHeightInVivo(Context context){\n        return QMUIDisplayHelper.dp2px(context, 27);\n    }\n\n    /**\n     * this method is private, because we do not need to handle tablet\n     *\n     * @param context\n     * @return\n     */\n    private static int getScreenRotation(Context context) {\n        WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        if (w == null) {\n            return Surface.ROTATION_0;\n        }\n        Display display = w.getDefaultDisplay();\n        if (display == null) {\n            return Surface.ROTATION_0;\n        }\n\n        return display.getRotation();\n    }\n\n    public static boolean isNotchOfficialSupport(){\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;\n    }\n\n    /**\n     * fitSystemWindows 对小米、vivo挖孔屏横屏挖孔区域无效\n     * @param view\n     * @return\n     */\n    public static boolean needFixLandscapeNotchAreaFitSystemWindow(View view){\n       return (QMUIDeviceHelper.isXiaomi() || QMUIDeviceHelper.isVivo()) && QMUINotchHelper.hasNotch(view);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIPackageHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\n\n/**\n * Created by Kayo on 2016/11/18.\n *\n */\npublic class QMUIPackageHelper {\n    private static String appVersionName;\n    private static String majorMinorVersion;\n    private static int majorVersion = -1;\n    private static int minorVersion = -1;\n    private static int fixVersion = -1;\n\n    /**\n     * manifest 中的 versionName 字段\n     */\n    public static String getAppVersion(Context context) {\n        if (appVersionName == null) {\n            PackageManager manager = context.getPackageManager();\n            try {\n                android.content.pm.PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);\n                appVersionName = info.versionName;\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n        if (appVersionName == null) {\n            return \"\";\n        } else {\n            return appVersionName;\n        }\n    }\n\n    /**\n     * 获取 App 的主版本与次版本号。比如说 3.1.2 中的 3.1\n     */\n    public static String getMajorMinorVersion(Context context) {\n        if (majorMinorVersion == null || majorMinorVersion.equals(\"\")) {\n            majorMinorVersion = getMajorVersion(context) + \".\" + getMinorVersion(context);\n        }\n        return majorMinorVersion;\n    }\n\n    /**\n     * 读取 App 的主版本号，例如 3.1.2，主版本号是 3\n     */\n    private static int getMajorVersion(Context context) {\n        if (majorVersion == -1) {\n            String appVersion = getAppVersion(context);\n            String[] parts = appVersion.split(\"\\\\.\");\n            if (parts.length != 0) {\n                majorVersion = Integer.parseInt(parts[0]);\n            }\n        }\n        return majorVersion;\n    }\n\n    /**\n     * 读取 App 的次版本号，例如 3.1.2，次版本号是 1\n     */\n    private static int getMinorVersion(Context context) {\n        if (minorVersion == -1) {\n            String appVersion = getAppVersion(context);\n            String[] parts = appVersion.split(\"\\\\.\");\n            if (parts.length >= 2) {\n                minorVersion = Integer.parseInt(parts[1]);\n            }\n        }\n        return minorVersion;\n    }\n\n    /**\n     * 读取 App 的修正版本号，例如 3.1.2，修正版本号是 2\n     */\n    public static int getFixVersion(Context context) {\n        if (fixVersion == -1) {\n            String appVersion = getAppVersion(context);\n            String[] parts = appVersion.split(\"\\\\.\");\n            if (parts.length >= 3) {\n                fixVersion = Integer.parseInt(parts[2]);\n            }\n        }\n        return fixVersion;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIReflectHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\n\n// Modify from https://github.com/didi/booster/blob/master/booster-android-instrument/src/main/java/com/didiglobal/booster/instrument/Reflection.java\npublic class QMUIReflectHelper {\n    private static final String TAG = \"QMUIReflectHelper\";\n\n    private QMUIReflectHelper() {\n    }\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T getStaticFieldValue(final Class<?> cls, final String name) {\n        if (null != cls && null != name) {\n            try {\n                final Field field = getField(cls, name);\n                if (null != field) {\n                    field.setAccessible(true);\n                    return (T) field.get(cls);\n                }\n            } catch (final Throwable t) {\n                Log.w(TAG, \"get static field \" + name + \" of \" + cls + \" error\", t);\n            }\n        }\n\n        return null;\n    }\n\n    public static boolean setStaticFieldValue(final Class<?> cls, final String name, final Object value) {\n        if (null != cls && null != name) {\n            try {\n                final Field field = getField(cls, name);\n                if (null != field) {\n                    field.setAccessible(true);\n                    field.set(cls, value);\n                    return true;\n                }\n            } catch (final Throwable t) {\n                Log.w(TAG, \"set static field \" + name + \" of \" + cls + \" error\", t);\n            }\n        }\n\n        return false;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T getFieldValue(final Object obj, final String name) {\n        if (null != obj && null != name) {\n            try {\n                final Field field = getField(obj.getClass(), name);\n                if (null != field) {\n                    field.setAccessible(true);\n                    return (T) field.get(obj);\n                }\n            } catch (final Throwable t) {\n                Log.w(TAG, \"get field \" + name + \" of \" + obj + \" error\", t);\n            }\n        }\n\n        return null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T getFieldValue(final Object obj, final Class<?> type) {\n        if (null != obj && null != type) {\n            try {\n                final Field field = getField(obj.getClass(), type);\n                if (null != field) {\n                    field.setAccessible(true);\n                    return (T) field.get(obj);\n                }\n            } catch (final Throwable t) {\n                Log.w(TAG, \"get field with type \" + type + \" of \" + obj + \" error\", t);\n            }\n        }\n\n        return null;\n    }\n\n    public static boolean setFieldValue(final Object obj, final String name, final Object value) {\n        if (null != obj && null != name) {\n            try {\n                final Field field = getField(obj.getClass(), name);\n                if (null != field) {\n                    field.setAccessible(true);\n                    field.set(obj, value);\n                    return true;\n                }\n            } catch (final Throwable t) {\n                Log.w(TAG, \"set field \" + name + \" of \" + obj + \" error\", t);\n            }\n        }\n\n        return false;\n    }\n\n    public static <T> T newInstance(final String className, final Object... args) {\n        try {\n            return newInstance(Class.forName(className), args);\n        } catch (final ClassNotFoundException e) {\n            Log.w(TAG, \"new instance of \" + className + \" error\", e);\n            return null;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T newInstance(final Class<?> clazz, Object... args) {\n        final Constructor<?>[] ctors = clazz.getDeclaredConstructors();\n\n        loop:\n        for (final Constructor<?> ctor : ctors) {\n            final Class<?>[] types = ctor.getParameterTypes();\n            if (types.length == args.length) {\n                for (int i = 0; i < types.length; i++) {\n                    if (null != args[i] && !types[i].isAssignableFrom(args[i].getClass())) {\n                        continue loop;\n                    }\n                }\n\n                try {\n                    ctor.setAccessible(true);\n                    return (T) ctor.newInstance(args);\n                } catch (final Throwable t) {\n                    Log.w(TAG, \"Invoke constructor \" + ctor + \" error\", t);\n                    return null;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeStaticMethod(final Class<?> klass, final String name) {\n        return invokeStaticMethod(klass, name, new Class[0], new Object[0]);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeStaticMethod(final Class<?> klass, final String name, final Class[] types, final Object[] args) {\n        if (null != klass && null != name && null != types && null != args && types.length == args.length) {\n            try {\n                final Method method = getMethod(klass, name, types);\n                if (null != method) {\n                    method.setAccessible(true);\n                    return (T) method.invoke(klass, args);\n                }\n            } catch (final Throwable e) {\n                Log.w(TAG, \"Invoke \" + name + \"(\" + Arrays.toString(types) + \") of \" + klass + \" error\", e);\n            }\n        }\n\n        return null;\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeMethod(final Object obj, final String name) {\n        return invokeMethod(obj, name, new Class[0], new Object[0]);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeMethod(final Object obj, final String name, final Class[] types, final Object[] args) {\n        if (null != obj && null != name && null != types && null != args && types.length == args.length) {\n            try {\n                final Method method = getMethod(obj.getClass(), name, types);\n                if (null != method) {\n                    method.setAccessible(true);\n                    return (T) method.invoke(obj, args);\n                }\n            } catch (final Throwable e) {\n                Log.w(TAG, \"Invoke \" + name + \"(\" + Arrays.toString(types) + \") of \" + obj + \" error\", e);\n            }\n        }\n\n        return null;\n    }\n\n    public static Field getField(final Class<?> cls, final String name) {\n        try {\n            return cls.getDeclaredField(name);\n        } catch (final NoSuchFieldException e) {\n            final Class<?> parent = cls.getSuperclass();\n            if (null == parent) {\n                return null;\n            }\n            return getField(parent, name);\n        }\n    }\n\n    public static Field getField(final Class<?> cls, final Class<?> type) {\n        final Field[] fields = cls.getDeclaredFields();\n        if (fields.length <= 0) {\n            final Class<?> parent = cls.getSuperclass();\n            if (null == parent) {\n                return null;\n            }\n            return getField(parent, type);\n        }\n\n        for (final Field field : fields) {\n            if (field.getType() == type) {\n                return field;\n            }\n        }\n\n        return null;\n    }\n\n    private static Method getMethod(final Class<?> cls, final String name, final Class<?>[] types) {\n        try {\n            return cls.getDeclaredMethod(name, types);\n        } catch (final NoSuchMethodException e) {\n            final Class<?> parent = cls.getSuperclass();\n            if (null == parent) {\n                return null;\n            }\n            return getMethod(parent, name, types);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIResHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.util.TypedValue;\nimport android.widget.TextView;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.R;\n\n/**\n * @author cginechen\n * @date 2016-09-22\n */\npublic class QMUIResHelper {\n    private static TypedValue sTmpValue;\n\n    public static float getAttrFloatValue(Context context, int attr) {\n        return getAttrFloatValue(context.getTheme(), attr);\n    }\n\n    public static float getAttrFloatValue(Resources.Theme theme, int attr) {\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!theme.resolveAttribute(attr, sTmpValue, true)) {\n            return 0;\n        }\n        return sTmpValue.getFloat();\n    }\n\n    public static int getAttrColor(Context context, int attrRes) {\n        return getAttrColor(context.getTheme(), attrRes);\n    }\n\n    public static int getAttrColor(Resources.Theme theme, int attr) {\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!theme.resolveAttribute(attr, sTmpValue, true)) {\n            return 0;\n        }\n        if (sTmpValue.type == TypedValue.TYPE_ATTRIBUTE) {\n            return getAttrColor(theme, sTmpValue.data);\n        }\n        return sTmpValue.data;\n    }\n\n    @Nullable\n    public static ColorStateList getAttrColorStateList(Context context, int attrRes) {\n        return getAttrColorStateList(context, context.getTheme(), attrRes);\n    }\n\n    @Nullable\n    public static ColorStateList getAttrColorStateList(Context context, Resources.Theme theme, int attr) {\n        if (attr == 0) {\n            return null;\n        }\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!theme.resolveAttribute(attr, sTmpValue, true)) {\n            return null;\n        }\n        if (sTmpValue.type >= TypedValue.TYPE_FIRST_COLOR_INT\n                && sTmpValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {\n            return ColorStateList.valueOf(sTmpValue.data);\n        }\n        if (sTmpValue.type == TypedValue.TYPE_ATTRIBUTE) {\n            return getAttrColorStateList(context, theme, sTmpValue.data);\n        }\n        if (sTmpValue.resourceId == 0) {\n            return null;\n        }\n        return ContextCompat.getColorStateList(context, sTmpValue.resourceId);\n    }\n\n    @Nullable\n    public static Drawable getAttrDrawable(Context context, int attr) {\n        return getAttrDrawable(context, context.getTheme(), attr);\n    }\n\n    @Nullable\n    public static Drawable getAttrDrawable(Context context, Resources.Theme theme, int attr) {\n        if (attr == 0) {\n            return null;\n        }\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!theme.resolveAttribute(attr, sTmpValue, true)) {\n            return null;\n        }\n        if (sTmpValue.type >= TypedValue.TYPE_FIRST_COLOR_INT\n                && sTmpValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {\n            return new ColorDrawable(sTmpValue.data);\n        }\n        if (sTmpValue.type == TypedValue.TYPE_ATTRIBUTE) {\n            return getAttrDrawable(context, theme, sTmpValue.data);\n        }\n\n        if (sTmpValue.resourceId != 0) {\n            return QMUIDrawableHelper.getVectorDrawable(context, sTmpValue.resourceId);\n        }\n        return null;\n    }\n\n    @Nullable\n    public static Drawable getAttrDrawable(Context context, TypedArray typedArray, int index) {\n        TypedValue value = typedArray.peekValue(index);\n        if (value != null) {\n            if (value.type != TypedValue.TYPE_ATTRIBUTE && value.resourceId != 0) {\n                return QMUIDrawableHelper.getVectorDrawable(context, value.resourceId);\n            }\n        }\n        return null;\n    }\n\n    public static int getAttrDimen(Context context, int attrRes) {\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!context.getTheme().resolveAttribute(attrRes, sTmpValue, true)) {\n            return 0;\n        }\n        return TypedValue.complexToDimensionPixelSize(sTmpValue.data, QMUIDisplayHelper.getDisplayMetrics(context));\n    }\n\n    @Nullable\n    public static String getAttrString(Context context, int attrRes) {\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        if (!context.getTheme().resolveAttribute(attrRes, sTmpValue, true)) {\n            return null;\n        }\n        CharSequence str = sTmpValue.string;\n        return str == null ? null : str.toString();\n    }\n\n    public static int getAttrInt(Context context, int attrRes) {\n        if (sTmpValue == null) {\n            sTmpValue = new TypedValue();\n        }\n        context.getTheme().resolveAttribute(attrRes, sTmpValue, true);\n        return sTmpValue.data;\n    }\n\n\n    public static void assignTextViewWithAttr(TextView textView, int attrRes) {\n        TypedArray a = textView.getContext().obtainStyledAttributes(null, R.styleable.QMUITextCommonStyleDef, attrRes, 0);\n        int count = a.getIndexCount();\n        int paddingLeft = textView.getPaddingLeft(), paddingRight = textView.getPaddingRight(),\n                paddingTop = textView.getPaddingTop(), paddingBottom = textView.getPaddingBottom();\n        for (int i = 0; i < count; i++) {\n            int attr = a.getIndex(i);\n            if (attr == R.styleable.QMUITextCommonStyleDef_android_gravity) {\n                textView.setGravity(a.getInt(attr, -1));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_textColor) {\n                textView.setTextColor(a.getColorStateList(attr));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_textSize) {\n                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, a.getDimensionPixelSize(attr, 0));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_paddingLeft) {\n                paddingLeft = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_paddingRight) {\n                paddingRight = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_paddingTop) {\n                paddingTop = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_paddingBottom) {\n                paddingBottom = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_singleLine) {\n                textView.setSingleLine(a.getBoolean(attr, false));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_ellipsize) {\n                int ellipsize = a.getInt(attr, 3);\n                switch (ellipsize) {\n                    case 1:\n                        textView.setEllipsize(TextUtils.TruncateAt.START);\n                        break;\n                    case 2:\n                        textView.setEllipsize(TextUtils.TruncateAt.MIDDLE);\n                        break;\n                    case 3:\n                        textView.setEllipsize(TextUtils.TruncateAt.END);\n                        break;\n                    case 4:\n                        textView.setEllipsize(TextUtils.TruncateAt.MARQUEE);\n                        break;\n                }\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_maxLines) {\n                textView.setMaxLines(a.getInt(attr, -1));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_background) {\n                QMUIViewHelper.setBackgroundKeepingPadding(textView, a.getDrawable(attr));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_lineSpacingExtra) {\n                textView.setLineSpacing(a.getDimensionPixelSize(attr, 0), 1f);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_drawablePadding) {\n                textView.setCompoundDrawablePadding(a.getDimensionPixelSize(attr, 0));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_textColorHint) {\n                textView.setHintTextColor(a.getColor(attr, 0));\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_textStyle) {\n                int styleIndex = a.getInt(attr, -1);\n                textView.setTypeface(null, styleIndex);\n            }\n        }\n        textView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);\n        a.recycle();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUISpanHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.graphics.drawable.Drawable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.span.QMUIAlignMiddleImageSpan;\nimport com.qmuiteam.qmui.span.QMUIMarginImageSpan;\n\nimport androidx.annotation.Nullable;\n\n/**\n * @author cginechen\n * @date 2016-10-12\n */\n\npublic class QMUISpanHelper {\n\n    /**\n     * 在text左边或者右边添加icon,\n     * 默认TextView添加leftDrawable或rightDrawable不能适应TextView match_parent的情况\n     *\n     * @param left true 则在文字左边添加 icon，false 则在文字右边添加 icon\n     * @param text 文字内容\n     * @param icon 需要被添加的 icon\n     * @return 返回带有 icon 的文字\n     */\n    public static CharSequence generateSideIconText(boolean left,\n                                                    int iconPadding, CharSequence text, Drawable icon) {\n        return generateSideIconText(\n                left, iconPadding, text, icon, 0);\n    }\n\n    public static CharSequence generateSideIconText(boolean left,\n                                                    int iconPadding, CharSequence text, Drawable icon,\n                                                    int iconOffsetY){\n        return generateSideIconText(\n                left, iconPadding, text, icon, iconOffsetY, 0, null);\n    }\n\n    public static CharSequence generateSideIconText(boolean left,\n                                                    int iconPadding, CharSequence text, Drawable icon,\n                                                    int iconTintAttr, @Nullable View skinFollowView){\n        return generateSideIconText(\n                left, iconPadding, text, icon, 0, iconTintAttr, skinFollowView);\n    }\n\n    public static CharSequence generateSideIconText(boolean left,\n                                                    int iconPadding, CharSequence text, Drawable icon,\n                                                    int iconOffsetY, int iconTintAttr,\n                                                    @Nullable View skinFollowView) {\n        return generateHorIconText(text,\n                left ? iconPadding : 0, left ? icon : null, left ? iconTintAttr : 0,\n                left ? 0 : iconPadding, left ? null : icon, left ? 0 : iconTintAttr,\n                iconOffsetY, skinFollowView);\n    }\n\n\n\n    public static CharSequence generateHorIconText(CharSequence text,\n                                                   int leftPadding, Drawable iconLeft,\n                                                   int rightPadding, Drawable iconRight) {\n        return generateHorIconText(text, leftPadding, iconLeft, rightPadding, iconRight,0);\n    }\n\n\n    public static CharSequence generateHorIconText(CharSequence text,\n                                                   int leftPadding, Drawable iconLeft,\n                                                   int rightPadding, Drawable iconRight,\n                                                   int iconOffsetY) {\n        return generateHorIconText(text, leftPadding, iconLeft, 0,\n                rightPadding, iconRight, 0, iconOffsetY, null);\n    }\n\n    public static CharSequence generateHorIconText(CharSequence text,\n                                                   int leftPadding, Drawable iconLeft, int iconLeftTintAttr,\n                                                   int rightPadding, Drawable iconRight, int iconRightTintAttr,\n                                                   @Nullable View skinFollowView) {\n        return generateHorIconText(text, leftPadding, iconLeft, iconLeftTintAttr,\n                rightPadding, iconRight, iconRightTintAttr,0, skinFollowView);\n    }\n\n    public static CharSequence generateHorIconText(CharSequence text,\n                                                   int leftPadding, Drawable iconLeft, int iconLeftTintAttr,\n                                                   int rightPadding, Drawable iconRight, int iconRightTintAttr,\n                                                   int iconOffsetY,\n                                                   @Nullable View skinFollowView) {\n        if (iconLeft == null && iconRight == null) {\n            return text;\n        }\n        String iconTag = \"[icon]\";\n        SpannableStringBuilder builder = new SpannableStringBuilder();\n        int start, end;\n        if (iconLeft != null) {\n            iconLeft.setBounds(0, 0, iconLeft.getIntrinsicWidth(), iconLeft.getIntrinsicHeight());\n            start = 0;\n            builder.append(iconTag);\n            end = builder.length();\n\n            QMUIMarginImageSpan imageSpan = new QMUIMarginImageSpan(iconLeft,\n                    QMUIAlignMiddleImageSpan.ALIGN_MIDDLE, 0, leftPadding, iconOffsetY);\n            imageSpan.setSkinSupportWithTintColor(skinFollowView, iconLeftTintAttr);\n            imageSpan.setAvoidSuperChangeFontMetrics(true);\n            builder.setSpan(imageSpan, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n        }\n\n        builder.append(text);\n        if (iconRight != null) {\n            iconRight.setBounds(0, 0, iconRight.getIntrinsicWidth(), iconRight.getIntrinsicHeight());\n            start = builder.length();\n            builder.append(iconTag);\n            end = builder.length();\n\n            QMUIMarginImageSpan imageSpan = new QMUIMarginImageSpan(iconRight,\n                    QMUIAlignMiddleImageSpan.ALIGN_MIDDLE, rightPadding, 0, iconOffsetY);\n            imageSpan.setSkinSupportWithTintColor(skinFollowView, iconRightTintAttr);\n            imageSpan.setAvoidSuperChangeFontMetrics(true);\n            builder.setSpan(imageSpan, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n        }\n\n        return builder;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIStatusBarHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport androidx.annotation.ColorInt;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowCompat;\nimport androidx.core.view.WindowInsetsControllerCompat;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\n\n\n\n/**\n * @author cginechen\n * @date 2016-03-27\n */\npublic class QMUIStatusBarHelper {\n\n    private enum StatusBarType {\n        Default, Miui, Flyme, Android6\n    }\n\n    private final static int STATUS_BAR_DEFAULT_HEIGHT_DP = 25; // 大部分状态栏都是25dp\n    // 在某些机子上存在不同的density值，所以增加两个虚拟值\n    public static float sVirtualDensity = -1;\n    public static float sVirtualDensityDpi = -1;\n    private static int sStatusBarHeight = -1;\n    private static StatusBarType mStatusBarType = StatusBarType.Default;\n    private static Integer sTransparentValue;\n\n    public static void translucent(Activity activity) {\n        translucent(activity.getWindow());\n    }\n\n    public static void translucent(Window window) {\n        translucent(window, 0x40000000);\n    }\n\n    private static boolean supportTranslucent() {\n        // Essential Phone 在 Android 8 之前沉浸式做得不全，系统不从状态栏顶部开始布局却会下发 WindowInsets\n        return !(QMUIDeviceHelper.isEssentialPhone() && Build.VERSION.SDK_INT < 26);\n    }\n\n    /**\n     * 沉浸式状态栏。\n     * 支持 4.4 以上版本的 MIUI 和 Flyme，以及 5.0 以上版本的其他 Android。\n     *\n     * @param activity 需要被设置沉浸式状态栏的 Activity。\n     */\n    public static void translucent(Activity activity, @ColorInt int colorOn5x) {\n        Window window = activity.getWindow();\n        translucent(window, colorOn5x);\n    }\n\n    @TargetApi(19)\n    public static void translucent(Window window, @ColorInt int colorOn5x) {\n        if (!supportTranslucent()) {\n            // 版本小于4.4，绝对不考虑沉浸式\n            return;\n        }\n\n        if (QMUINotchHelper.isNotchOfficialSupport()) {\n            handleDisplayCutoutMode(window);\n        }\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            // 小米 Android 6.0 ，开发版 7.7.13 及以后版本设置黑色字体又需要 clear FLAG_TRANSLUCENT_STATUS, 因此还原为官方模式\n            if (QMUIDeviceHelper.isFlymeLowerThan(8) || (QMUIDeviceHelper.isMIUI() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M)) {\n                window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,\n                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n                return;\n            }\n        }\n\n        int systemUiVisibility = window.getDecorView().getSystemUiVisibility();\n        systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;\n        window.getDecorView().setSystemUiVisibility(systemUiVisibility);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            // android 6以后可以改状态栏字体颜色，因此可以自行设置为透明\n            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n            window.setStatusBarColor(Color.TRANSPARENT);\n        } else {\n            // android 5不能修改状态栏字体颜色，因此直接用FLAG_TRANSLUCENT_STATUS，nexus表现为半透明\n            // 魅族和小米的表现如何？\n            // update: 部分手机运用FLAG_TRANSLUCENT_STATUS时背景不是半透明而是没有背景了。。。。。\n//                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n\n            // 采取setStatusBarColor的方式，部分机型不支持，那就纯黑了，保证状态栏图标可见\n            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n            window.setStatusBarColor(colorOn5x);\n        }\n    }\n\n    /**\n     * 如果原本存在某一个flag， 就将它迁移到 out\n     * @param window\n     * @param out\n     * @param type\n     * @return\n     */\n    public static int retainSystemUiFlag(Window window, int out, int type) {\n        int now = window.getDecorView().getSystemUiVisibility();\n        if ((now & type) == type) {\n            out |= type;\n        }\n        return out;\n    }\n\n    @TargetApi(28)\n    private static void handleDisplayCutoutMode(final Window window) {\n        View decorView = window.getDecorView();\n        if (decorView != null) {\n            if (ViewCompat.isAttachedToWindow(decorView)) {\n                realHandleDisplayCutoutMode(window, decorView);\n            } else {\n                decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n                    @Override\n                    public void onViewAttachedToWindow(View v) {\n                        v.removeOnAttachStateChangeListener(this);\n                        realHandleDisplayCutoutMode(window, v);\n                    }\n\n                    @Override\n                    public void onViewDetachedFromWindow(View v) {\n\n                    }\n                });\n            }\n        }\n    }\n\n    @TargetApi(28)\n    private static void realHandleDisplayCutoutMode(Window window, View decorView) {\n        if (decorView.getRootWindowInsets() != null &&\n                decorView.getRootWindowInsets().getDisplayCutout() != null) {\n            WindowManager.LayoutParams params = window.getAttributes();\n            params.layoutInDisplayCutoutMode = WindowManager.LayoutParams\n                    .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;\n            window.setAttributes(params);\n        }\n    }\n\n    /**\n     * 设置状态栏黑色字体图标，\n     * 支持 4.4 以上版本 MIUI 和 Flyme，以及 6.0 以上版本的其他 Android\n     *\n     * @param activity 需要被处理的 Activity\n     */\n    public static boolean setStatusBarLightMode(Activity activity) {\n        if (activity == null) return false;\n\n        if (mStatusBarType != StatusBarType.Default) {\n            return setStatusBarLightMode(activity, mStatusBarType);\n        }\n        if (isMIUICustomStatusBarLightModeImpl() && MIUISetStatusBarLightMode(activity.getWindow(), true)) {\n            mStatusBarType = StatusBarType.Miui;\n            return true;\n        } else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {\n            mStatusBarType = StatusBarType.Flyme;\n            return true;\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            Android6SetStatusBarLightMode(activity.getWindow(), true);\n            mStatusBarType = StatusBarType.Android6;\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 已知系统类型时，设置状态栏黑色字体图标。\n     * 支持 4.4 以上版本 MIUI 和 Flyme，以及 6.0 以上版本的其他 Android\n     *\n     * @param activity 需要被处理的 Activity\n     * @param type     StatusBar 类型，对应不同的系统\n     */\n    private static boolean setStatusBarLightMode(Activity activity, StatusBarType type) {\n        if (type == StatusBarType.Miui) {\n            return MIUISetStatusBarLightMode(activity.getWindow(), true);\n        } else if (type == StatusBarType.Flyme) {\n            return FlymeSetStatusBarLightMode(activity.getWindow(), true);\n        } else if (type == StatusBarType.Android6) {\n            return Android6SetStatusBarLightMode(activity.getWindow(), true);\n        }\n        return false;\n    }\n\n\n    /**\n     * 设置状态栏白色字体图标\n     * 支持 4.4 以上版本 MIUI 和 Flyme，以及 6.0 以上版本的其他 Android\n     */\n    public static boolean setStatusBarDarkMode(Activity activity) {\n        if (activity == null) return false;\n        if (mStatusBarType == StatusBarType.Default) {\n            // 默认状态，不需要处理\n            return true;\n        }\n\n        if (mStatusBarType == StatusBarType.Miui) {\n            return MIUISetStatusBarLightMode(activity.getWindow(), false);\n        } else if (mStatusBarType == StatusBarType.Flyme) {\n            return FlymeSetStatusBarLightMode(activity.getWindow(), false);\n        } else if (mStatusBarType == StatusBarType.Android6) {\n            return Android6SetStatusBarLightMode(activity.getWindow(), false);\n        }\n        return true;\n    }\n\n    /**\n     * 设置状态栏字体图标为深色，Android 6\n     *\n     * @param window 需要设置的窗口\n     * @param light  是否把状态栏字体及图标颜色设置为深色\n     * @return boolean 成功执行返回true\n     */\n    @TargetApi(23)\n    private static boolean Android6SetStatusBarLightMode(Window window, boolean light) {\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {\n            WindowInsetsControllerCompat insetsController = WindowCompat.getInsetsController(window, window.getDecorView());\n            if (insetsController != null) {\n                insetsController.setAppearanceLightStatusBars(light);\n            }\n        } else {\n            // 经过测试，小米 Android 11 用  WindowInsetsControllerCompat 不起作用， 我还能说什么呢。。。\n            View decorView = window.getDecorView();\n            int systemUi = decorView.getSystemUiVisibility();\n            if (light) {\n                systemUi |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;\n            } else {\n                systemUi &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;\n            }\n            decorView.setSystemUiVisibility(systemUi);\n        }\n\n        if (QMUIDeviceHelper.isMIUIV9()) {\n            // MIUI 9 低于 6.0 版本依旧只能回退到以前的方案\n            // https://github.com/Tencent/QMUI_Android/issues/160\n            MIUISetStatusBarLightMode(window, light);\n        }\n        return true;\n    }\n\n    /**\n     * 设置状态栏字体图标为深色，需要 MIUIV6 以上\n     *\n     * @param window 需要设置的窗口\n     * @param light  是否把状态栏字体及图标颜色设置为深色\n     * @return boolean 成功执行返回 true\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static boolean MIUISetStatusBarLightMode(Window window, boolean light) {\n        boolean result = false;\n        if (window != null) {\n            Class clazz = window.getClass();\n            try {\n                int darkModeFlag;\n                Class layoutParams = Class.forName(\"android.view.MiuiWindowManager$LayoutParams\");\n                Field field = layoutParams.getField(\"EXTRA_FLAG_STATUS_BAR_DARK_MODE\");\n                darkModeFlag = field.getInt(layoutParams);\n                Method extraFlagField = clazz.getMethod(\"setExtraFlags\", int.class, int.class);\n                if (light) {\n                    extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体\n                } else {\n                    extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体\n                }\n                result = true;\n            } catch (Exception ignored) {\n\n            }\n        }\n        return result;\n    }\n\n    /**\n     * 更改状态栏图标、文字颜色的方案是否是MIUI自家的， MIUI9 && Android 6 之后用回Android原生实现\n     * 见小米开发文档说明：https://dev.mi.com/console/doc/detail?pId=1159\n     */\n    private static boolean isMIUICustomStatusBarLightModeImpl() {\n        if (QMUIDeviceHelper.isMIUIV9() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            return true;\n        }\n        return QMUIDeviceHelper.isMIUIV5() || QMUIDeviceHelper.isMIUIV6() ||\n                QMUIDeviceHelper.isMIUIV7() || QMUIDeviceHelper.isMIUIV8();\n    }\n\n    /**\n     * 设置状态栏图标为深色和魅族特定的文字风格\n     * 可以用来判断是否为 Flyme 用户\n     *\n     * @param window 需要设置的窗口\n     * @param light  是否把状态栏字体及图标颜色设置为深色\n     * @return boolean 成功执行返回true\n     */\n    public static boolean FlymeSetStatusBarLightMode(Window window, boolean light) {\n        boolean result = false;\n        if (window != null) {\n\n            Android6SetStatusBarLightMode(window, light);\n\n            // flyme 在 6.2.0.0A 支持了 Android 官方的实现方案，旧的方案失效\n            // 高版本调用这个出现不可预期的 Bug,官方文档也没有给出完整的高低版本兼容方案\n            if (QMUIDeviceHelper.isFlymeLowerThan(7)) {\n                try {\n                    WindowManager.LayoutParams lp = window.getAttributes();\n                    Field darkFlag = WindowManager.LayoutParams.class\n                            .getDeclaredField(\"MEIZU_FLAG_DARK_STATUS_BAR_ICON\");\n                    Field meizuFlags = WindowManager.LayoutParams.class\n                            .getDeclaredField(\"meizuFlags\");\n                    darkFlag.setAccessible(true);\n                    meizuFlags.setAccessible(true);\n                    int bit = darkFlag.getInt(null);\n                    int value = meizuFlags.getInt(lp);\n                    if (light) {\n                        value |= bit;\n                    } else {\n                        value &= ~bit;\n                    }\n                    meizuFlags.setInt(lp, value);\n                    window.setAttributes(lp);\n                    result = true;\n                } catch (Exception ignored) {\n\n                }\n            } else if (QMUIDeviceHelper.isFlyme()) {\n                result = true;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * 获取是否全屏\n     *\n     * @return 是否全屏\n     */\n    public static boolean isFullScreen(Activity activity) {\n        boolean ret = false;\n        try {\n            WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();\n            ret = (attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return ret;\n    }\n\n    /**\n     * API19之前透明状态栏：获取设置透明状态栏的system ui visibility的值，这是部分有提供接口的rom使用的\n     * http://stackoverflow.com/questions/21865621/transparent-status-bar-before-4-4-kitkat\n     */\n    public static Integer getStatusBarAPITransparentValue(Context context) {\n        if (sTransparentValue != null) {\n            return sTransparentValue;\n        }\n        String[] systemSharedLibraryNames = context.getPackageManager()\n                .getSystemSharedLibraryNames();\n        String fieldName = null;\n        for (String lib : systemSharedLibraryNames) {\n            if (\"touchwiz\".equals(lib)) {\n                fieldName = \"SYSTEM_UI_FLAG_TRANSPARENT_BACKGROUND\";\n            } else if (lib.startsWith(\"com.sonyericsson.navigationbar\")) {\n                fieldName = \"SYSTEM_UI_FLAG_TRANSPARENT\";\n            }\n        }\n\n        if (fieldName != null) {\n            try {\n                Field field = View.class.getField(fieldName);\n                if (field != null) {\n                    Class<?> type = field.getType();\n                    if (type == int.class) {\n                        sTransparentValue = field.getInt(null);\n                    }\n                }\n            } catch (Exception ignored) {\n            }\n        }\n        return sTransparentValue;\n    }\n\n    /**\n     * 获取状态栏的高度。\n     */\n    public static int getStatusbarHeight(Context context) {\n        if (sStatusBarHeight == -1) {\n            initStatusBarHeight(context);\n        }\n        return sStatusBarHeight;\n    }\n\n    private static void initStatusBarHeight(Context context) {\n        Class<?> clazz;\n        Object obj = null;\n        Field field = null;\n        try {\n            clazz = Class.forName(\"com.android.internal.R$dimen\");\n            obj = clazz.newInstance();\n            if (QMUIDeviceHelper.isMeizu()) {\n                try {\n                    field = clazz.getField(\"status_bar_height_large\");\n                } catch (Throwable t) {\n                    t.printStackTrace();\n                }\n            }\n            if (field == null) {\n                field = clazz.getField(\"status_bar_height\");\n            }\n        } catch (Throwable t) {\n            t.printStackTrace();\n        }\n        if (field != null && obj != null) {\n            try {\n                int id = Integer.parseInt(field.get(obj).toString());\n                sStatusBarHeight = context.getResources().getDimensionPixelSize(id);\n            } catch (Throwable t) {\n                t.printStackTrace();\n            }\n        }\n        if (sStatusBarHeight <= 0) {\n            if (sVirtualDensity == -1) {\n                sStatusBarHeight = QMUIDisplayHelper.dp2px(context, STATUS_BAR_DEFAULT_HEIGHT_DP);\n            } else {\n                sStatusBarHeight = (int) (STATUS_BAR_DEFAULT_HEIGHT_DP * sVirtualDensity + 0.5f);\n            }\n        }\n    }\n\n    public static void setVirtualDensity(float density) {\n        sVirtualDensity = density;\n    }\n\n    public static void setVirtualDensityDpi(float densityDpi) {\n        sVirtualDensityDpi = densityDpi;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIToastHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\n\n// Modify from https://github.com/didi/booster/blob/master/booster-android-instrument-toast/src/main/java/com/didiglobal/booster/instrument/ShadowToast.java\npublic class QMUIToastHelper {\n    private static final String TAG = \"QMUIToastHelper\";\n    public static void show(Toast toast){\n        if(Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1){\n            fixToastForAndroidN(toast).show();\n        }else{\n            toast.show();\n        }\n    }\n\n    private static Toast fixToastForAndroidN(Toast toast){\n        Object tn = QMUIReflectHelper.getFieldValue(toast, \"mTN\");\n        if(tn == null){\n            Log.w(TAG, \"The value of field mTN of \" + toast + \" is null\");\n            return toast;\n        }\n        Object handler = QMUIReflectHelper.getFieldValue(tn, \"mHandler\");\n        if(handler instanceof Handler){\n            if(QMUIReflectHelper.setFieldValue(\n                    handler, \"mCallback\", new FixCallback((Handler) handler))){\n                return toast;\n            }\n        }\n\n        final Object show = QMUIReflectHelper.getFieldValue(tn, \"mShow\");\n        if (show instanceof Runnable) {\n            if (QMUIReflectHelper.setFieldValue(tn, \"mShow\", new FixRunnable((Runnable) show))) {\n                return toast;\n            }\n        }\n        Log.w(TAG, \"Neither field mHandler nor mShow of \" + tn + \" is accessible\");\n        return toast;\n    }\n\n    public static class FixCallback implements Handler.Callback {\n\n        private final Handler mHandler;\n\n        public FixCallback(final Handler handler) {\n            mHandler = handler;\n        }\n\n        @Override\n        public boolean handleMessage(@NonNull Message msg) {\n            try {\n                mHandler.handleMessage(msg);\n            } catch (Throwable e) {\n                // ignore\n            }\n            return true;\n        }\n    }\n\n    public static class FixRunnable implements Runnable {\n\n        private final Runnable mRunnable;\n\n        public FixRunnable(final Runnable runnable) {\n            mRunnable = runnable;\n        }\n\n        @Override\n        public void run() {\n            try {\n                mRunnable.run();\n            } catch (final RuntimeException e) {\n                // ignore\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIViewHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ArgbEvaluator;\nimport android.animation.ObjectAnimator;\nimport android.animation.ValueAnimator;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.LightingColorFilter;\nimport android.graphics.Matrix;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.view.TouchDelegate;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.Window;\nimport android.view.animation.AlphaAnimation;\nimport android.view.animation.Animation;\nimport android.view.animation.DecelerateInterpolator;\nimport android.view.animation.TranslateAnimation;\nimport android.widget.ImageView;\nimport android.widget.ListView;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * @author cginechen\n * @date 2016-03-17\n */\npublic class QMUIViewHelper {\n\n    // copy from View.generateViewId for API <= 16\n    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);\n\n\n    private static final int[] APPCOMPAT_CHECK_ATTRS = {\n            androidx.appcompat.R.attr.colorPrimary\n    };\n\n    public static void checkAppCompatTheme(Context context) {\n        TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);\n        final boolean failed = !a.hasValue(0);\n        a.recycle();\n        if (failed) {\n            throw new IllegalArgumentException(\"You need to use a Theme.AppCompat theme \"\n                    + \"(or descendant) with the design library.\");\n        }\n    }\n\n    /**\n     * 获取activity的根view\n     */\n    public static View getActivityRoot(Activity activity) {\n        return ((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).getChildAt(0);\n    }\n\n    /**\n     * 触发window的insets的广播，使得view的fitSystemWindows得以生效\n     */\n    @SuppressWarnings(\"deprecation\")\n    public static void requestApplyInsets(Window window) {\n        if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {\n            window.getDecorView().requestFitSystemWindows();\n        } else if (Build.VERSION.SDK_INT >= 21) {\n            window.getDecorView().requestApplyInsets();\n        }\n    }\n\n    /**\n     * 扩展点击区域的范围\n     *\n     * @param view       需要扩展的元素，此元素必需要有父级元素\n     * @param expendSize 需要扩展的尺寸（以sp为单位的）\n     */\n    public static void expendTouchArea(final View view, final int expendSize) {\n        if (view != null) {\n            final View parentView = (View) view.getParent();\n\n            parentView.post(new Runnable() {\n                @Override\n                public void run() {\n                    Rect rect = new Rect();\n                    view.getHitRect(rect); //如果太早执行本函数，会获取rect失败，因为此时UI界面尚未开始绘制，无法获得正确的坐标\n                    rect.left -= expendSize;\n                    rect.top -= expendSize;\n                    rect.right += expendSize;\n                    rect.bottom += expendSize;\n                    parentView.setTouchDelegate(new TouchDelegate(rect, view));\n                }\n            });\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    public static void setBackground(View view, Drawable drawable) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            view.setBackground(drawable);\n        } else {\n            view.setBackgroundDrawable(drawable);\n        }\n    }\n\n    public static void setBackgroundKeepingPadding(View view, Drawable drawable) {\n        int[] padding = new int[]{view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()};\n        view.setBackground(drawable);\n        view.setPadding(padding[0], padding[1], padding[2], padding[3]);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static void setBackgroundKeepingPadding(View view, int backgroundResId) {\n        setBackgroundKeepingPadding(view, ContextCompat.getDrawable(view.getContext(), backgroundResId));\n    }\n\n    public static void setBackgroundColorKeepPadding(View view, @ColorInt int color) {\n        int[] padding = new int[]{view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()};\n        view.setBackgroundColor(color);\n        view.setPadding(padding[0], padding[1], padding[2], padding[3]);\n    }\n\n    /**\n     * 对 View 的做背景闪动的动画\n     */\n    public static void playBackgroundBlinkAnimation(final View v, @ColorInt int bgColor) {\n        if (v == null) {\n            return;\n        }\n        int[] alphaArray = new int[]{0, 255, 0};\n        playViewBackgroundAnimation(v, bgColor, alphaArray, 300);\n    }\n\n    /**\n     * 对 View 做背景色变化的动作\n     *\n     * @param v            做背景色变化的View\n     * @param bgColor      背景色\n     * @param alphaArray   背景色变化的alpha数组，如 int[]{255,0} 表示从纯色变化到透明\n     * @param stepDuration 每一步变化的时长\n     * @param endAction    动画结束后的回调\n     */\n    public static Animator playViewBackgroundAnimation(final View v, @ColorInt int bgColor, int[] alphaArray, int stepDuration, final Runnable endAction) {\n        int animationCount = alphaArray.length - 1;\n\n        Drawable bgDrawable = new ColorDrawable(bgColor);\n        final Drawable oldBgDrawable = v.getBackground();\n        setBackgroundKeepingPadding(v, bgDrawable);\n\n        List<Animator> animatorList = new ArrayList<>();\n        for (int i = 0; i < animationCount; i++) {\n            ObjectAnimator animator = ObjectAnimator.ofInt(v.getBackground(), \"alpha\", alphaArray[i], alphaArray[i + 1]);\n            animatorList.add(animator);\n        }\n\n        AnimatorSet animatorSet = new AnimatorSet();\n        animatorSet.setDuration(stepDuration);\n        animatorSet.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                setBackgroundKeepingPadding(v, oldBgDrawable);\n                if (endAction != null) {\n                    endAction.run();\n                }\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n            }\n        });\n        animatorSet.playSequentially(animatorList);\n        animatorSet.start();\n        return animatorSet;\n    }\n\n    public static void playViewBackgroundAnimation(final View v, @ColorInt int bgColor, int[] alphaArray, int stepDuration) {\n        playViewBackgroundAnimation(v, bgColor, alphaArray, stepDuration, null);\n    }\n\n    /**\n     * 对 View 做背景色变化的动作\n     *\n     * @param v            做背景色变化的View\n     * @param startColor   动画开始时 View 的背景色\n     * @param endColor     动画结束时 View 的背景色\n     * @param duration     动画总时长\n     * @param repeatCount  动画重复次数\n     * @param setAnimTagId 将动画设置tag给view,若为0则不设置\n     * @param endAction    动画结束后的回调\n     */\n    public static void playViewBackgroundAnimation(final View v, @ColorInt int startColor, @ColorInt int endColor, long duration, int repeatCount, int setAnimTagId, final Runnable endAction) {\n        final Drawable oldBgDrawable = v.getBackground(); // 存储旧的背景\n        QMUIViewHelper.setBackgroundColorKeepPadding(v, startColor);\n        final ValueAnimator anim = new ValueAnimator();\n        anim.setIntValues(startColor, endColor);\n        anim.setDuration(duration / (repeatCount + 1));\n        anim.setRepeatCount(repeatCount);\n        anim.setRepeatMode(ValueAnimator.REVERSE);\n        anim.setEvaluator(new ArgbEvaluator());\n        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                QMUIViewHelper.setBackgroundColorKeepPadding(v, (Integer) animation.getAnimatedValue());\n            }\n        });\n        if (setAnimTagId != 0) {\n            v.setTag(setAnimTagId, anim);\n        }\n        anim.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                setBackgroundKeepingPadding(v, oldBgDrawable);\n                if (endAction != null) {\n                    endAction.run();\n                }\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        });\n        anim.start();\n    }\n\n    public static void playViewBackgroundAnimation(final View v, int startColor, int endColor, long duration) {\n        playViewBackgroundAnimation(v, startColor, endColor, duration, 0, 0, null);\n    }\n\n    public static int generateViewId() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return View.generateViewId();\n        } else {\n            for (; ; ) {\n                final int result = sNextGeneratedId.get();\n                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.\n                int newValue = result + 1;\n                if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.\n                if (sNextGeneratedId.compareAndSet(result, newValue)) {\n                    return result;\n                }\n            }\n        }\n    }\n\n    /**\n     * <p>对 View 做透明度变化的进场动画。</p>\n     * <p>相关方法 {@link #fadeOut(View, int, Animation.AnimationListener, boolean)}</p>\n     *\n     * @param view            做动画的 View\n     * @param duration        动画时长(毫秒)\n     * @param listener        动画回调\n     * @param isNeedAnimation 是否需要动画\n     */\n    public static AlphaAnimation fadeIn(View view, int duration, Animation.AnimationListener listener, boolean isNeedAnimation) {\n        if (view == null) {\n            return null;\n        }\n        if (isNeedAnimation) {\n            view.setVisibility(View.VISIBLE);\n            AlphaAnimation alpha = new AlphaAnimation(0, 1);\n            alpha.setInterpolator(new DecelerateInterpolator());\n            alpha.setDuration(duration);\n            alpha.setFillAfter(true);\n            if (listener != null) {\n                alpha.setAnimationListener(listener);\n            }\n            view.startAnimation(alpha);\n            return alpha;\n        } else {\n            view.setAlpha(1);\n            view.setVisibility(View.VISIBLE);\n            return null;\n        }\n    }\n\n    /**\n     * <p>对 View 做透明度变化的退场动画</p>\n     * <p>相关方法 {@link #fadeIn(View, int, Animation.AnimationListener, boolean)}</p>\n     *\n     * @param view            做动画的 View\n     * @param duration        动画时长(毫秒)\n     * @param listener        动画回调\n     * @param isNeedAnimation 是否需要动画\n     */\n    public static AlphaAnimation fadeOut(final View view, int duration, final Animation.AnimationListener listener, boolean isNeedAnimation) {\n        if (view == null) {\n            return null;\n        }\n        if (isNeedAnimation) {\n            AlphaAnimation alpha = new AlphaAnimation(1, 0);\n            alpha.setInterpolator(new DecelerateInterpolator());\n            alpha.setDuration(duration);\n            alpha.setAnimationListener(new Animation.AnimationListener() {\n                @Override\n                public void onAnimationStart(Animation animation) {\n                    if (listener != null) {\n                        listener.onAnimationStart(animation);\n                    }\n                }\n\n                @Override\n                public void onAnimationEnd(Animation animation) {\n                    view.setVisibility(View.GONE);\n                    if (listener != null) {\n                        listener.onAnimationEnd(animation);\n                    }\n                }\n\n                @Override\n                public void onAnimationRepeat(Animation animation) {\n                    if (listener != null) {\n                        listener.onAnimationRepeat(animation);\n                    }\n                }\n            });\n            view.startAnimation(alpha);\n            return alpha;\n        } else {\n            view.setVisibility(View.GONE);\n            return null;\n        }\n    }\n\n    public static void clearValueAnimator(Animator animator) {\n        if (animator != null) {\n            animator.removeAllListeners();\n            if (animator instanceof ValueAnimator) {\n                ((ValueAnimator) animator).removeAllUpdateListeners();\n            }\n\n            if (Build.VERSION.SDK_INT >= 19) {\n                animator.pause();\n            }\n            animator.cancel();\n        }\n    }\n\n    public static Rect calcViewScreenLocation(View view) {\n        int[] location = new int[2];\n        // 获取控件在屏幕中的位置，返回的数组分别为控件左顶点的 x、y 的值\n        view.getLocationOnScreen(location);\n        return new Rect(location[0], location[1], location[0] + view.getWidth(),\n                location[1] + view.getHeight());\n    }\n\n    /**\n     * <p>对 View 做上下位移的进场动画</p>\n     * <p>相关方法 {@link #slideOut(View, int, Animation.AnimationListener, boolean, QMUIDirection)}</p>\n     *\n     * @param view            做动画的 View\n     * @param duration        动画时长(毫秒)\n     * @param listener        动画回调\n     * @param isNeedAnimation 是否需要动画\n     * @param direction       进场动画的方向\n     * @return 动画对应的 Animator 对象, 注意无动画时返回 null\n     */\n    public static\n    @Nullable\n    TranslateAnimation slideIn(final View view, int duration, final Animation.AnimationListener listener, boolean isNeedAnimation, QMUIDirection direction) {\n        if (view == null) {\n            return null;\n        }\n        if (isNeedAnimation) {\n            TranslateAnimation translate = null;\n            switch (direction) {\n                case LEFT_TO_RIGHT:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n                case TOP_TO_BOTTOM:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n                case RIGHT_TO_LEFT:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n                case BOTTOM_TO_TOP:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n            }\n            translate.setInterpolator(new DecelerateInterpolator());\n            translate.setDuration(duration);\n            translate.setFillAfter(true);\n            if (listener != null) {\n                translate.setAnimationListener(listener);\n            }\n            view.setVisibility(View.VISIBLE);\n            view.startAnimation(translate);\n            return translate;\n        } else {\n            view.clearAnimation();\n            view.setVisibility(View.VISIBLE);\n\n            return null;\n        }\n    }\n\n    /**\n     * <p>对 View 做上下位移的退场动画</p>\n     * <p>相关方法 {@link #slideIn(View, int, Animation.AnimationListener, boolean, QMUIDirection)}</p>\n     *\n     * @param view            做动画的 View\n     * @param duration        动画时长(毫秒)\n     * @param listener        动画回调\n     * @param isNeedAnimation 是否需要动画\n     * @param direction       进场动画的方向\n     * @return 动画对应的 Animator 对象, 注意无动画时返回 null\n     */\n    public static\n    @Nullable\n    TranslateAnimation slideOut(final View view, int duration, final Animation.AnimationListener listener, boolean isNeedAnimation, QMUIDirection direction) {\n        if (view == null) {\n            return null;\n        }\n        if (isNeedAnimation) {\n            TranslateAnimation translate = null;\n            switch (direction) {\n                case LEFT_TO_RIGHT:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n                case TOP_TO_BOTTOM:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f\n                    );\n                    break;\n                case RIGHT_TO_LEFT:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f\n                    );\n                    break;\n                case BOTTOM_TO_TOP:\n                    translate = new TranslateAnimation(\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,\n                            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1f\n                    );\n                    break;\n            }\n            translate.setInterpolator(new DecelerateInterpolator());\n            translate.setDuration(duration);\n            translate.setAnimationListener(new Animation.AnimationListener() {\n                @Override\n                public void onAnimationStart(Animation animation) {\n                    if (listener != null) {\n                        listener.onAnimationStart(animation);\n                    }\n                }\n\n                @Override\n                public void onAnimationEnd(Animation animation) {\n                    view.setVisibility(View.GONE);\n                    if (listener != null) {\n                        listener.onAnimationEnd(animation);\n                    }\n                }\n\n                @Override\n                public void onAnimationRepeat(Animation animation) {\n                    if (listener != null) {\n                        listener.onAnimationRepeat(animation);\n                    }\n                }\n            });\n            view.startAnimation(translate);\n            return translate;\n        } else {\n            view.clearAnimation();\n            view.setVisibility(View.GONE);\n            return null;\n        }\n\n    }\n\n    /**\n     * 对 View 设置 paddingLeft\n     *\n     * @param view  需要被设置的 View\n     * @param value 设置的值\n     */\n    public static void setPaddingLeft(View view, int value) {\n        if (value != view.getPaddingLeft()) {\n            view.setPadding(value, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());\n        }\n    }\n\n    /**\n     * 对 View 设置 paddingTop\n     *\n     * @param view  需要被设置的 View\n     * @param value 设置的值\n     */\n    public static void setPaddingTop(View view, int value) {\n        if (value != view.getPaddingTop()) {\n            view.setPadding(view.getPaddingLeft(), value, view.getPaddingRight(), view.getPaddingBottom());\n        }\n    }\n\n    /**\n     * 对 View 设置 paddingRight\n     *\n     * @param view  需要被设置的 View\n     * @param value 设置的值\n     */\n    public static void setPaddingRight(View view, int value) {\n        if (value != view.getPaddingRight()) {\n            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), value, view.getPaddingBottom());\n        }\n    }\n\n    /**\n     * 对 View 设置 paddingBottom\n     *\n     * @param view  需要被设置的 View\n     * @param value 设置的值\n     */\n    public static void setPaddingBottom(View view, int value) {\n        if (value != view.getPaddingBottom()) {\n            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), value);\n        }\n    }\n\n    public static void updateChildrenOffsetHelperOnLayout(@NonNull ViewGroup viewGroup){\n        View view;\n        QMUIViewOffsetHelper offsetHelper;\n        for(int i = 0; i < viewGroup.getChildCount(); i++){\n            view = viewGroup.getChildAt(i);\n            offsetHelper = getOffsetHelper(view);\n            if(offsetHelper != null){\n                offsetHelper.onViewLayout();\n            }\n        }\n    }\n\n    @Nullable\n    public static QMUIViewOffsetHelper getOffsetHelper(@NonNull View view){\n        Object tag = view.getTag(R.id.qmui_view_offset_helper);\n        if(tag instanceof QMUIViewOffsetHelper){\n            return (QMUIViewOffsetHelper) tag;\n        }\n        return null;\n    }\n\n    @NonNull\n    public static QMUIViewOffsetHelper getOrCreateOffsetHelper(@NonNull View view){\n        Object tag = view.getTag(R.id.qmui_view_offset_helper);\n        if(tag instanceof QMUIViewOffsetHelper){\n            return (QMUIViewOffsetHelper) tag;\n        }else{\n            QMUIViewOffsetHelper ret = new QMUIViewOffsetHelper(view);\n            view.setTag(R.id.qmui_view_offset_helper, ret);\n            return ret;\n        }\n    }\n\n    /**\n     * requestDisallowInterceptTouchEvent 的安全方法。存在它的原因是 QMUIPullRefreshLayout 会拦截这个事件\n     *\n     * @param view\n     * @param value\n     */\n    public static void safeRequestDisallowInterceptTouchEvent(@NonNull View view, boolean value) {\n        ViewParent viewParent = view.getParent();\n        if (viewParent != null) {\n            ViewParent layout = viewParent;\n            while (layout != null) {\n                if (layout instanceof QMUIPullRefreshLayout) {\n                    ((QMUIPullRefreshLayout) layout).openSafeDisallowInterceptTouchEvent();\n                }\n                layout = layout.getParent();\n            }\n            viewParent.requestDisallowInterceptTouchEvent(value);\n        }\n    }\n\n    public static void safeSetImageViewSelected(ImageView imageView, boolean selected) {\n        // imageView setSelected 实现有问题。\n        // resizeFromDrawable 中判断 drawable size 是否改变而调用 requestLayout，看似合理，但不会被调用\n        // 因为 super.setSelected(selected) 会调用 refreshDrawableState\n        // 而从 android 6 以后， ImageView 会重载refreshDrawableState，并在里面处理了 drawable size 改变的问题,\n        // 从而导致 resizeFromDrawable 的判断失效\n        Drawable drawable = imageView.getDrawable();\n        if (drawable == null) {\n            return;\n        }\n        int drawableWidth = drawable.getIntrinsicWidth();\n        int drawableHeight = drawable.getIntrinsicHeight();\n        imageView.setSelected(selected);\n        if (drawable.getIntrinsicWidth() != drawableWidth || drawable.getIntrinsicHeight() != drawableHeight) {\n            imageView.requestLayout();\n        }\n    }\n\n\n    /**\n     * please use ImageViewCompat.setImageTintList() replace this.\n     */\n    @Deprecated\n    public static ColorFilter setImageViewTintColor(ImageView imageView, @ColorInt int tintColor) {\n        LightingColorFilter colorFilter = new LightingColorFilter(Color.argb(255, 0, 0, 0), tintColor);\n        imageView.setColorFilter(colorFilter);\n        return colorFilter;\n    }\n\n    /**\n     * 判断 ListView 是否已经滚动到底部。\n     *\n     * @param listView 需要被判断的 ListView。\n     * @return ListView 已经滚动到底部则返回 true，否则返回 false。\n     */\n    public static boolean isListViewAlreadyAtBottom(ListView listView) {\n        if (listView.getAdapter() == null || listView.getHeight() == 0) {\n            return false;\n        }\n\n        if (listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1) {\n            View lastItemView = listView.getChildAt(listView.getChildCount() - 1);\n            if (lastItemView != null && lastItemView.getBottom() == listView.getHeight()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n    /**\n     * Retrieve the transformed bounding rect of an arbitrary descendant view.\n     * This does not need to be a direct child.\n     *\n     * @param descendant descendant view to reference\n     * @param out        rect to set to the bounds of the descendant view\n     */\n    public static void getDescendantRect(ViewGroup parent, View descendant, Rect out) {\n        out.set(0, 0, descendant.getWidth(), descendant.getHeight());\n        ViewGroupHelper.offsetDescendantRect(parent, descendant, out);\n    }\n\n    public static boolean getDescendantVisibleRect(ViewGroup target, View descendant, Rect out){\n        out.set(0, 0, descendant.getWidth(), descendant.getHeight());\n        ViewParent parent = descendant.getParent();\n        View next = descendant;\n        while (parent instanceof ViewGroup && parent != target){\n            final ViewGroup vp = (ViewGroup) parent;\n            ViewGroupHelper.offsetDescendantRect(vp, next, out);\n            if(out.left >= vp.getWidth() || out.right <= 0 || out.top >= vp.getHeight() || out.bottom <= 0){\n                return false;\n            }\n            if(out.left < 0){\n                out.left = 0;\n            }\n            if(out.right > vp.getWidth()){\n                out.right = vp.getWidth();\n            }\n            if(out.top < 0){\n                out.top = 0;\n            }\n            if(out.bottom > vp.getHeight()){\n                out.bottom = vp.getHeight();\n            }\n            next = vp;\n            parent = parent.getParent();\n        }\n        return out.left < target.getWidth() && out.right > 0 && out.top < target.getHeight() && out.bottom > 0;\n    }\n\n    private static class ViewGroupHelper {\n        private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();\n        private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();\n\n        public static void offsetDescendantRect(ViewGroup group, View child, Rect rect) {\n            Matrix m = sMatrix.get();\n            if (m == null) {\n                m = new Matrix();\n                sMatrix.set(m);\n            } else {\n                m.reset();\n            }\n\n            m.preTranslate(-group.getScrollX(), -group.getScrollY());\n            offsetDescendantMatrix(group, child, m);\n\n            RectF rectF = sRectF.get();\n            if (rectF == null) {\n                rectF = new RectF();\n                sRectF.set(rectF);\n            }\n            rectF.set(rect);\n            m.mapRect(rectF);\n            rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),\n                    (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));\n        }\n\n        static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {\n            final ViewParent parent = view.getParent();\n            if (parent instanceof View && parent != target) {\n                final View vp = (View) parent;\n                offsetDescendantMatrix(target, vp, m);\n                m.preTranslate(-vp.getScrollX(), -vp.getScrollY());\n            }\n\n            m.preTranslate(view.getLeft(), view.getTop());\n\n            if (!view.getMatrix().isIdentity()) {\n                m.preConcat(view.getMatrix());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIViewOffsetHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2015 The Android Open Source Project\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.qmuiteam.qmui.util;\n\nimport android.view.View;\n\nimport androidx.core.view.ViewCompat;\n\n/**\n * Utility helper for moving a {@link View} around using\n * {@link View#offsetLeftAndRight(int)} and\n * {@link View#offsetTopAndBottom(int)}.\n * <p>\n * Also the setting of absolute offsets (similar to translationX/Y), rather than additive\n * offsets.\n */\npublic final class QMUIViewOffsetHelper {\n\n    private final View mView;\n\n    private int mLayoutTop;\n    private int mLayoutLeft;\n    private int mOffsetTop;\n    private int mOffsetLeft;\n\n    private boolean mVerticalOffsetEnabled = true;\n    private boolean mHorizontalOffsetEnabled = true;\n\n    public QMUIViewOffsetHelper(View view) {\n        mView = view;\n    }\n\n    public void onViewLayout() {\n        onViewLayout(true);\n    }\n\n    public void onViewLayout(boolean applyOffset) {\n        mLayoutTop = mView.getTop();\n        mLayoutLeft = mView.getLeft();\n        if(applyOffset){\n            applyOffsets();\n        }\n    }\n\n    public void applyOffsets() {\n        ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));\n        ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));\n    }\n\n    /**\n     * Set the top and bottom offset for this {@link QMUIViewOffsetHelper}'s view.\n     *\n     * @param offset the offset in px.\n     * @return true if the offset has changed\n     */\n    public boolean setTopAndBottomOffset(int offset) {\n        if (mVerticalOffsetEnabled && mOffsetTop != offset) {\n            mOffsetTop = offset;\n            applyOffsets();\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Set the left and right offset for this {@link QMUIViewOffsetHelper}'s view.\n     *\n     * @param offset the offset in px.\n     * @return true if the offset has changed\n     */\n    public boolean setLeftAndRightOffset(int offset) {\n        if (mHorizontalOffsetEnabled && mOffsetLeft != offset) {\n            mOffsetLeft = offset;\n            applyOffsets();\n            return true;\n        }\n        return false;\n    }\n\n    public boolean setOffset(int leftOffset, int topOffset) {\n        if(!mHorizontalOffsetEnabled && !mVerticalOffsetEnabled){\n            return false;\n        }else if(mHorizontalOffsetEnabled && mVerticalOffsetEnabled){\n            if (mOffsetLeft != leftOffset || mOffsetTop != topOffset) {\n                mOffsetLeft = leftOffset;\n                mOffsetTop = topOffset;\n                applyOffsets();\n                return true;\n            }\n            return false;\n        }else if(mHorizontalOffsetEnabled){\n            return setLeftAndRightOffset(leftOffset);\n        }else{\n            return setTopAndBottomOffset(topOffset);\n        }\n    }\n\n    public int getTopAndBottomOffset() {\n        return mOffsetTop;\n    }\n\n    public int getLeftAndRightOffset() {\n        return mOffsetLeft;\n    }\n\n    public int getLayoutTop() {\n        return mLayoutTop;\n    }\n\n    public int getLayoutLeft() {\n        return mLayoutLeft;\n    }\n\n    public void setHorizontalOffsetEnabled(boolean horizontalOffsetEnabled) {\n        mHorizontalOffsetEnabled = horizontalOffsetEnabled;\n    }\n\n    public boolean isHorizontalOffsetEnabled() {\n        return mHorizontalOffsetEnabled;\n    }\n\n    public void setVerticalOffsetEnabled(boolean verticalOffsetEnabled) {\n        mVerticalOffsetEnabled = verticalOffsetEnabled;\n    }\n\n    public boolean isVerticalOffsetEnabled() {\n        return mVerticalOffsetEnabled;\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIWindowHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.view.View;\nimport android.view.ViewParent;\nimport android.view.WindowManager;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.QMUIConfig;\n\nimport java.lang.reflect.Field;\n\n/**\n * @author cginechen\n * @date 2016-08-05\n */\npublic class QMUIWindowHelper {\n\n    public static final int KEYBOARD_HEIGHT_BOUNDARY_DP = 100;\n\n\n    /**\n     * 设置WindowManager.LayoutParams的type\n     * <p>\n     * 1.使用 type 值为 TYPE_PHONE 和TYPE_SYSTEM_ALERT 需要申请 SYSTEM_ALERT_WINDOW 权限\n     * 2.type 值为 TYPE_TOAST 显示的 System overlay view 不需要权限，即可在任何平台显示。\n     * 3.type 值为 TYPE_TOAST在API level 19 以下因无法接收无法接收触摸（点击)和按键事件\n     * 4.Android 6.0 悬浮窗被默认被禁用，即使申请了 SYSTEM_ALERT_WINDOW 权限，应用也会crash,需要用户自己去开启\n     * （开启路径：通用 -- 应用管理 -- 更多 -- 配置应用 --- 在其他应用的上层显示 --- 选择你的APP -- 运行在其他应用的上层显示）\n     * 5. 不直接返回type而是传layoutParams是不想调用者增加 @SuppressWarnings({\"ResourceType\"}) 跳过编译器的检查\n     */\n\n    public static void setWindowType(WindowManager.LayoutParams layoutParams) {\n        layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;\n    }\n\n\n    @Nullable\n    @SuppressWarnings({\"JavaReflectionMemberAccess\"})\n    public static Rect unSafeGetWindowVisibleInsets(@NonNull View view) {\n        Object attachInfo = getAttachInfoFromView(view);\n        if (attachInfo == null) {\n            return null;\n        }\n        try {\n            // fortunately now it is in light greylist, just be warned.\n            Field visibleInsetsField = attachInfo.getClass().getDeclaredField(\"mVisibleInsets\");\n            visibleInsetsField.setAccessible(true);\n            Object visibleInsets = visibleInsetsField.get(attachInfo);\n            if (visibleInsets instanceof Rect) {\n                return (Rect) visibleInsets;\n            }\n        } catch (Throwable e) {\n            if (QMUIConfig.DEBUG) {\n                e.printStackTrace();\n            }\n        }\n        return null;\n    }\n\n    @Nullable\n    @SuppressWarnings({\"JavaReflectionMemberAccess\"})\n    public static Rect unSafeGetContentInsets(@NonNull View view) {\n        Object attachInfo = getAttachInfoFromView(view);\n        if (attachInfo == null) {\n            return null;\n        }\n        try {\n            // fortunately now it is in light greylist, just be warned.\n            Field visibleInsetsField = attachInfo.getClass().getDeclaredField(\"mContentInsets\");\n            visibleInsetsField.setAccessible(true);\n            Object visibleInsets = visibleInsetsField.get(attachInfo);\n            if (visibleInsets instanceof Rect) {\n                return (Rect) visibleInsets;\n            }\n        } catch (Throwable e) {\n            if (QMUIConfig.DEBUG) {\n                e.printStackTrace();\n            }\n        }\n        return null;\n    }\n\n    public static Object getAttachInfoFromView(@NonNull View view) {\n        Object attachInfo = null;\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {\n            // Android 10+ can not reflect the View.mAttachInfo\n            // fortunately now it is in light greylist in ViewRootImpl\n            View rootView = view.getRootView();\n            if (rootView != null) {\n                ViewParent vp = rootView.getParent();\n                if (vp != null) {\n                    try {\n                        Field field = vp.getClass().getDeclaredField(\"mAttachInfo\");\n                        field.setAccessible(true);\n                        attachInfo = field.get(vp);\n                    } catch (Throwable e) {\n                        if (QMUIConfig.DEBUG) {\n                            e.printStackTrace();\n                        }\n                    }\n                }\n            }\n        } else {\n            try {\n                // Android P forbid the reflection for @hide filed,\n                // fortunately now it is in light greylist, just be warned.\n                Field field = View.class.getDeclaredField(\"mAttachInfo\");\n                field.setAccessible(true);\n                attachInfo = field.get(view);\n            } catch (Throwable e) {\n                if (QMUIConfig.DEBUG) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return attachInfo;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/util/QMUIWindowInsetHelper.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.util;\n\nimport android.os.Build;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowInsets;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.OnApplyWindowInsetsListener;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.R;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\npublic class QMUIWindowInsetHelper {\n\n    public final static InsetHandler consumeInsetWithPaddingHandler = new InsetHandler() {\n        @Override\n        public void handleInset(View view, Insets insets) {\n            view.setPadding(insets.left, insets.top, insets.right, insets.bottom);\n        }\n    };\n\n    public final static InsetHandler consumeInsetWithPaddingIgnoreBottomHandler = new InsetHandler() {\n        @Override\n        public void handleInset(View view, Insets insets) {\n            view.setPadding(insets.left, insets.top, insets.right, 0);\n        }\n    };\n\n    public final static InsetHandler consumeInsetWithPaddingIgnoreTopHandler = new InsetHandler() {\n        @Override\n        public void handleInset(View view, Insets insets) {\n            view.setPadding(insets.left, 0, insets.right, insets.bottom);\n        }\n    };\n\n    public final static InsetHandler consumeInsetWithPaddingWithGravityHandler = new InsetHandler() {\n        @Override\n        public void handleInset(View view, Insets insets) {\n            Insets toUsed = adapterInsetsWithGravity(view, insets);\n            view.setPadding(toUsed.left, toUsed.top, toUsed.right, toUsed.bottom);\n        }\n    };\n\n    private final static OnApplyWindowInsetsListener sStopDispatchListener = new OnApplyWindowInsetsListener() {\n        @Override\n        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {\n            return WindowInsetsCompat.CONSUMED;\n        }\n    };\n\n    private final static OnApplyWindowInsetsListener sOverrideWithNothingHandleListener = new OnApplyWindowInsetsListener() {\n        @Override\n        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {\n            return insets;\n        }\n    };\n\n    public static void handleWindowInsets(View v, @WindowInsetsCompat.Type.InsetsType final int insetsType){\n        handleWindowInsets(v, insetsType, false);\n    }\n\n    public static void handleWindowInsets(View v, @WindowInsetsCompat.Type.InsetsType final int insetsType, boolean jumpSelfHandleIfMatchLast){\n        handleWindowInsets(v, insetsType, jumpSelfHandleIfMatchLast, false);\n    }\n\n    public static void handleWindowInsets(View v, @WindowInsetsCompat.Type.InsetsType final int insetsType, boolean jumpSelfHandleIfMatchLast, boolean ignoreVisibility){\n        handleWindowInsets(v, insetsType, consumeInsetWithPaddingWithGravityHandler, jumpSelfHandleIfMatchLast, ignoreVisibility, false);\n    }\n\n    public static void handleWindowInsets(View v, @WindowInsetsCompat.Type.InsetsType final int insetsType,\n                                          boolean jumpSelfHandleIfMatchLast,\n                                          boolean ignoreVisibility,\n                                          boolean stopDispatch){\n        handleWindowInsets(v, insetsType, consumeInsetWithPaddingWithGravityHandler, jumpSelfHandleIfMatchLast, ignoreVisibility, stopDispatch);\n    }\n\n    /**\n     *\n     * @param v the view to handle window insets.\n     * @param insetsType the insets type\n     * @param insetHandler insetHandler\n     * @param jumpSelfHandleIfMatchLast if same as last, we do not dispatch window insets to v but return the last result directly.\n     * @param stopDispatch it's dangerous to use this. if View.sBrokenInsetsDispatch is true, it will stop dispatching to siblings and children,\n     *                     if View.sBrokenInsetsDispatch is false, it will only stop dispatching to children. But View.sBrokenInsetsDispatch is\n     *                     not public.\n     */\n    public static void handleWindowInsets(View v,\n                                          @WindowInsetsCompat.Type.InsetsType final int insetsType,\n                                          @NonNull final InsetHandler insetHandler,\n                                          boolean jumpSelfHandleIfMatchLast,\n                                          final boolean ignoreVisibility,\n                                          final boolean stopDispatch\n    ){\n        setOnApplyWindowInsetsListener(v, new androidx.core.view.OnApplyWindowInsetsListener() {\n            @Override\n            public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {\n                if(v.getFitsSystemWindows()){\n                    Insets toUsed = ignoreVisibility ? insets.getInsetsIgnoringVisibility(insetsType) : insets.getInsets(insetsType);\n                    insetHandler.handleInset(v, toUsed);\n                    if(stopDispatch){\n                        return WindowInsetsCompat.CONSUMED;\n                    }\n                }\n                return insets;\n            }\n        }, jumpSelfHandleIfMatchLast);\n    }\n\n    /**\n     * it's dangerous to use this. if View.sBrokenInsetsDispatch is true, it will stop dispatching to siblings and children,\n     * if View.sBrokenInsetsDispatch is false, it will only stop dispatching to children. But View.sBrokenInsetsDispatch is\n     * not public.\n     * @param v the view to stop\n     */\n    public static void stopDispatchWindowInsets(View v){\n        setOnApplyWindowInsetsListener(v, sStopDispatchListener, true);\n    }\n\n    public static void overrideWithDoNotHandleWindowInsets(View v){\n        setOnApplyWindowInsetsListener(v, sOverrideWithNothingHandleListener, false);\n    }\n\n    // copy from ViewCompat 1.5.0-beta01,  fix the re dispatch problem.\n    public static void setOnApplyWindowInsetsListener(final @NonNull View v,\n                                               final @Nullable OnApplyWindowInsetsListener listener,\n                                               final boolean reuseIfInputIsSame\n    ) {\n        // For backward compatibility of WindowInsetsAnimation, we use an\n        // OnApplyWindowInsetsListener. We use the view tags to keep track of both listeners\n        if (Build.VERSION.SDK_INT < 30) {\n            v.setTag(R.id.tag_on_apply_window_listener, listener);\n        }\n\n        if (listener == null) {\n            // If the listener is null, we need to make sure our compat listener, if any, is\n            // set in-lieu of the listener being removed.\n            View.OnApplyWindowInsetsListener compatInsetsAnimationCallback =\n                    (View.OnApplyWindowInsetsListener) v.getTag(\n                            R.id.tag_window_insets_animation_callback);\n            v.setOnApplyWindowInsetsListener(compatInsetsAnimationCallback);\n            return;\n        }\n\n        v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {\n            WindowInsetsCompat mLastInsets = null;\n            WindowInsets mReturnedInsets = null;\n\n            @Override\n            public WindowInsets onApplyWindowInsets(final View view,\n                                                    final WindowInsets insets) {\n                WindowInsetsCompat compatInsets = WindowInsetsCompat.toWindowInsetsCompat(\n                        insets, view);\n                // On API < 30,  we request dispatch again until the input is same with last.\n                boolean needRequestApplyInsetsAgain = true;\n                if (Build.VERSION.SDK_INT < 30) {\n                    callCompatInsetAnimationCallback(insets, v);\n\n                    if (compatInsets.equals(mLastInsets)) {\n                        needRequestApplyInsetsAgain = false;\n                        if (reuseIfInputIsSame) {\n                            // We got the same insets we just return the previously computed insets.\n                            return mReturnedInsets;\n                        }\n                    }\n                    mLastInsets = compatInsets;\n                }\n                compatInsets = listener.onApplyWindowInsets(view, compatInsets);\n\n                if (Build.VERSION.SDK_INT >= 30) {\n                    return compatInsets.toWindowInsets();\n                }\n\n                // On API < 30, the visibleInsets, used to built WindowInsetsCompat, are\n                // updated after the insets dispatch so we don't have the updated visible\n                // insets at that point. As a workaround, we re-apply the insets so we know\n                // that we'll have the right value the next time it's called.\n                if(needRequestApplyInsetsAgain){\n                    ViewCompat.requestApplyInsets(view);\n                }\n\n                // Keep a copy in case the insets haven't changed on the next call so we don't\n                // need to call the listener again.\n                mReturnedInsets = compatInsets.toWindowInsets();\n                return mReturnedInsets;\n            }\n        });\n    }\n\n    /**\n     * The backport of {@link WindowInsetsAnimationCompat.Callback} on API < 30 relies on\n     * onApplyWindowInsetsListener, so if this callback is set, we'll call it in this method\n     */\n    private static void callCompatInsetAnimationCallback(final @NonNull WindowInsets insets,\n                                                 final @NonNull View v) {\n        // In case a WindowInsetsAnimationCompat.Callback is set, make sure to\n        // call its compat listener.\n        View.OnApplyWindowInsetsListener insetsAnimationCallback =\n                (View.OnApplyWindowInsetsListener) v.getTag(\n                        R.id.tag_window_insets_animation_callback);\n        if (insetsAnimationCallback != null) {\n            insetsAnimationCallback.onApplyWindowInsets(v, insets);\n        }\n    }\n\n    public static Insets adapterInsetsWithGravity(View view, Insets insets){\n        int left = insets.left;\n        int right = insets.right;\n        int top = insets.top;\n        int bottom = insets.bottom;\n\n        ViewGroup.LayoutParams lp = view.getLayoutParams();\n        if(lp instanceof ConstraintLayout.LayoutParams){\n            ConstraintLayout.LayoutParams constraintLp = (ConstraintLayout.LayoutParams) lp;\n            if (constraintLp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {\n                if(constraintLp.leftToLeft == ConstraintLayout.LayoutParams.PARENT_ID){\n                    right = 0;\n                }else if(constraintLp.rightToRight == ConstraintLayout.LayoutParams.PARENT_ID){\n                    left = 0;\n                }\n            }\n\n            if (constraintLp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {\n                if(constraintLp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID){\n                    bottom = 0;\n                }else if(constraintLp.bottomToBottom == ConstraintLayout.LayoutParams.PARENT_ID){\n                    top = 0;\n                }\n            }\n        }else{\n            int gravity = -1;\n            if (lp instanceof FrameLayout.LayoutParams) {\n                gravity = ((FrameLayout.LayoutParams) lp).gravity;\n            }\n            /**\n             * 因为该方法执行时机早于 FrameLayout.layoutChildren，\n             * 而在 {FrameLayout#layoutChildren} 中当 gravity == -1 时会设置默认值为 Gravity.TOP | Gravity.LEFT，\n             * 所以这里也要同样设置\n             */\n            if (gravity == -1) {\n                gravity = Gravity.TOP | Gravity.LEFT;\n            }\n\n            if (lp.width != ViewGroup.LayoutParams.MATCH_PARENT) {\n                int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\n                switch (horizontalGravity) {\n                    case Gravity.LEFT:\n                        right = 0;\n                        break;\n                    case Gravity.RIGHT:\n                        left = 0;\n                        break;\n                }\n            }\n\n            if (lp.height != ViewGroup.LayoutParams.MATCH_PARENT) {\n                int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;\n                switch (verticalGravity) {\n                    case Gravity.TOP:\n                        bottom = 0;\n                        break;\n                    case Gravity.BOTTOM:\n                        top = 0;\n                        break;\n                }\n            }\n        }\n        return Insets.of(left, top, right, bottom);\n    }\n\n    public interface InsetHandler{\n        void handleInset(View view, Insets insets);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/IBlankTouchDetector.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.widget;\n\nimport android.view.MotionEvent;\n\npublic interface IBlankTouchDetector {\n    boolean isTouchInBlank(MotionEvent event);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/IWindowInsetKeyboardConsumer.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\npublic interface IWindowInsetKeyboardConsumer {\n    void onHandleKeyboard(int keyboardInset);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIAnimationListView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.database.DataSetObserver;\nimport android.graphics.Canvas;\nimport android.os.Looper;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\nimport android.view.animation.Interpolator;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.BaseAdapter;\nimport android.widget.ListAdapter;\nimport android.widget.ListView;\n\nimport androidx.collection.LongSparseArray;\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.QMUILog;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 使 {@link ListView} 支持添加/删除 Item 的动画，支持自定义动画效果。\n * <p>\n * https://github.com/cypressious/AnimationListView/blob/master/AnimationListView/src/de/cypressworks/animationlistview/AnimationListView.java\n * <p>\n * 一个痛点：\n * 在LayoutTransition中有一个CHANGE_DISAPPEAR的概念：指由于添加或移动等操作导致子view消失的场景。\n * QMUIAnimationListView同样有这样的场景，但是ListView上LayoutTransition不生效，所以我们需要自己模拟实现。\n * 但是当layout后，消失的view已经被回收了，我们只能对 ListView 当前存在的view做动画，那么如何去做CHANGE_DISAPPEAR动画呢？\n * 这里采用了一个非常挫的实现方案（暂时没想到其它好的方案）：\n * 1.在layout前对当前屏幕的view都设置 setHasTransientState(true)。这样做后，view不会被直接被ListView回收到scrap中\n * 2.将当前屏幕的view都保存在mDetachViewsMap中\n * 3.在draw之前（这个时候我们已经能确定哪些item会完全离开屏幕了），剔除不会完全离开屏幕的item\n * 4.开启一个ValueAnimator,每次update时调用invalidate()触发 onDraw() 方法\n * 5.在 onDraw() 方法中根据animator的已动画时间计算view动画的位置，调用view.draw方法draw出来，因为是之前存在过的view，大小必定是确定的\n * 6.最后需要调用 setHasTransientState(false)，以便view最终可以被回收\n * <p>\n * 这种方法的代价就是：当前屏幕的item不能够被及时回收（最终还是会被回收的）\n * 所以增加 mOpenChangeDisappearAnimation 变量，如果你并不在意 CHANGE_DISAPPEAR 没有动画的那一点点不协调，那就不用开启它\n *\n * @author cginechen\n * @date 2017-03-30\n */\n\n@SuppressWarnings({\"rawtypes\", \"unchecked\"})\npublic class QMUIAnimationListView extends ListView {\n    private static final String TAG = \"QMUIAnimationListView\";\n    private static final long DURATION_ALPHA = 300;\n    private static final long DURATION_OFFSET_MIN = 0;\n    private static final long DURATION_OFFSET_MAX = 1000;\n    private static final float DEFAULT_OFFSET_DURATION_UNIT = 0.5f;\n\n    protected final LongSparseArray<Integer> mTopMap = new LongSparseArray<>();\n    protected final LongSparseArray<Integer> mPositionMap = new LongSparseArray<>();\n    protected final LongSparseArray<View> mDetachViewsMap = new LongSparseArray<>();\n    protected final Set<Long> mBeforeVisible = new HashSet<>();\n    protected final Set<Long> mAfterVisible = new HashSet<>();\n    private final List<Manipulator> mPendingManipulations = new ArrayList<>();\n    private final List<Manipulator> mPendingManipulationsWithoutAnimation = new ArrayList<>();\n    private long mChangeDisappearPlayTime = 0;\n    private ValueAnimator mChangeDisappearAnimator;\n\n    private ListAdapter mRealAdapter;\n    private WrapperAdapter mWrapperAdapter;\n    private boolean mIsAnimating = false;\n    private int mAnimationManipulateDurationLimit = 0;\n    private long mLastManipulateTime = 0;\n    private float mOffsetDurationUnit = DEFAULT_OFFSET_DURATION_UNIT; // 移动1px的时间\n    private Interpolator mOffsetInterpolator = new LinearInterpolator();\n    private boolean mOpenChangeDisappearAnimation = false;\n\n\n    public QMUIAnimationListView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIAnimationListView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public QMUIAnimationListView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    public QMUIAnimationListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n        init();\n    }\n\n    private void init() {\n        setWillNotDraw(false);\n    }\n\n    public ListAdapter getRealAdapter() {\n        return mRealAdapter;\n    }\n\n    @Override\n    public void setAdapter(ListAdapter adapter) {\n        mRealAdapter = adapter;\n        mWrapperAdapter = adapter != null ? new WrapperAdapter(mRealAdapter) : null;\n        super.setAdapter(mWrapperAdapter);\n    }\n\n    public void setAnimationManipulateDurationLimit(int animationManipulateDurationLimit) {\n        mAnimationManipulateDurationLimit = animationManipulateDurationLimit;\n    }\n\n    public <T extends ListAdapter> void manipulate(final Manipulator<T> manipulator) {\n        Log.i(TAG, \"manipulate\");\n        if (!mWrapperAdapter.isAnimationEnabled()) {\n            manipulateWithoutAnimation(manipulator);\n            return;\n        }\n        long now = SystemClock.uptimeMillis();\n        boolean notLimitedAnimation = now - mLastManipulateTime > mAnimationManipulateDurationLimit;\n        mLastManipulateTime = now;\n        if (!mIsAnimating) {\n            if (notLimitedAnimation) {\n                mIsAnimating = true;\n                prepareAnimation();\n                manipulator.manipulate((T) mRealAdapter);\n\n                doAnimation();\n            } else {\n                manipulator.manipulate((T) mRealAdapter);\n                mWrapperAdapter.notifyDataSetChanged();\n            }\n        } else {\n            if (notLimitedAnimation) {\n                mPendingManipulations.add(manipulator);\n            } else {\n                mPendingManipulationsWithoutAnimation.add(manipulator);\n            }\n        }\n    }\n\n    public <T extends ListAdapter> void manipulateWithoutAnimation(final Manipulator<T> manipulator) {\n        Log.i(TAG, \"manipulateWithoutAnimation\");\n        if (!mIsAnimating) {\n            manipulator.manipulate((T) mRealAdapter);\n            mWrapperAdapter.notifyDataSetChanged();\n        } else {\n            mPendingManipulationsWithoutAnimation.add(manipulator);\n        }\n    }\n\n    public float getOffsetDurationUnit() {\n        return mOffsetDurationUnit;\n    }\n\n    public void setOffsetDurationUnit(float offsetDurationUnit) {\n        mOffsetDurationUnit = offsetDurationUnit;\n    }\n\n    private long getOffsetDuration(int start, int end) {\n        long duration = (long) (Math.abs(start - end) * mOffsetDurationUnit);\n        return Math.max(DURATION_OFFSET_MIN, Math.min(duration, DURATION_OFFSET_MAX));\n    }\n\n    /**\n     * 是否启用 CHANGE-DISAPPEAR 动画。\n     *\n     * @param openChangeDisappearAnimation true 为启用 CHANGE-DISAPPEAR 动画，false 则不启用。\n     */\n    public void setOpenChangeDisappearAnimation(boolean openChangeDisappearAnimation) {\n        mOpenChangeDisappearAnimation = openChangeDisappearAnimation;\n    }\n\n    public void setOffsetInterpolator(Interpolator offsetInterpolator) {\n        mOffsetInterpolator = offsetInterpolator;\n    }\n\n    private void prepareAnimation() {\n        mTopMap.clear();\n        mPositionMap.clear();\n        mBeforeVisible.clear();\n        mAfterVisible.clear();\n        mDetachViewsMap.clear();\n\n        mWrapperAdapter.setShouldNotifyChange(false);\n        int childCount = getChildCount();\n        int firstVisiblePos = getFirstVisiblePosition();\n        for (int i = 0; i < childCount; i++) {\n            final View view = getChildAt(i);\n            long id = mWrapperAdapter.getItemId(firstVisiblePos + i);\n            mTopMap.put(id, view.getTop());\n            mPositionMap.put(id, i);\n        }\n\n        for (int i = 0; i < firstVisiblePos; i++) {\n            final long id = mWrapperAdapter.getItemId(i);\n            mBeforeVisible.add(id);\n        }\n\n        final int count = mWrapperAdapter.getCount();\n\n        for (int i = firstVisiblePos + childCount; i < count; i++) {\n            final long id = mWrapperAdapter.getItemId(i);\n            mAfterVisible.add(id);\n        }\n    }\n\n    private void doAnimation() {\n        setEnabled(false);\n        setClickable(false);\n        doPreLayoutAnimation(new ManipulateAnimatorListener() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                mWrapperAdapter.notifyDataSetChanged();\n                getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {\n\n                    @Override\n                    public boolean onPreDraw() {\n                        getViewTreeObserver().removeOnPreDrawListener(this);\n\n                        doPostLayoutAnimation();\n\n                        return true;\n                    }\n\n                });\n            }\n        });\n\n    }\n\n    private void doPreLayoutAnimation(Animator.AnimatorListener listener) {\n        final AnimatorSet animatorSet = new AnimatorSet();\n        ArrayList<Long> deleteIds = new ArrayList<>();\n        int i;\n        for (i = 0; i < mTopMap.size(); i++) {\n            long id = mTopMap.keyAt(i);\n            int newPos = getPositionForId(id);\n            if (newPos < 0) {\n                // delete\n                int oldPos = mPositionMap.get(id);\n                View child = getChildAt(oldPos);\n                final Animator anim = getDeleteAnimator(child);\n                mPositionMap.remove(id);\n                animatorSet.play(anim);\n                deleteIds.add(id);\n            }\n        }\n\n        for (i = 0; i < deleteIds.size(); i++) {\n            mTopMap.remove(deleteIds.get(i));\n        }\n\n        if (mOpenChangeDisappearAnimation) {\n            for (i = 0; i < mPositionMap.size(); i++) {\n                View view = getChildAt(mPositionMap.valueAt(i));\n                ViewCompat.setHasTransientState(view, true);\n                mDetachViewsMap.put(mPositionMap.keyAt(i), view);\n            }\n        }\n        if (!animatorSet.getChildAnimations().isEmpty()) {\n            animatorSet.addListener(listener);\n            animatorSet.start();\n        } else {\n            listener.onAnimationEnd(animatorSet);\n        }\n    }\n\n    private void doPostLayoutAnimation() {\n        final AnimatorSet animatorSet = new AnimatorSet();\n        int childCount = getChildCount();\n        int firstVisiblePos = getFirstVisiblePosition();\n        Animator anim = null;\n        int addOccurTop = -1;\n        int addOccurPosition = -1;\n        if (mOpenChangeDisappearAnimation) {\n            for (int i = 0; i < mDetachViewsMap.size(); i++) {\n                ViewCompat.setHasTransientState(mDetachViewsMap.valueAt(i), false);\n            }\n        }\n        for (int i = 0; i < childCount; i++) {\n            View child = getChildAt(i);\n            child.setAlpha(1f);\n            int newTop = child.getTop();\n            int position = firstVisiblePos + i;\n            long id = mWrapperAdapter.getItemId(position);\n            if (mTopMap.indexOfKey(id) > -1) {\n                addOccurTop = -1;\n                int oldTop = mTopMap.get(id);\n                mTopMap.remove(id);\n                mPositionMap.remove(id);\n                if (mOpenChangeDisappearAnimation) {\n                    mDetachViewsMap.remove(id);\n                }\n                if (oldTop != newTop) {\n                    anim = getOffsetAnimator(child, oldTop, newTop);\n                }\n            } else if (mBeforeVisible.contains(id)) {\n                addOccurTop = -1;\n                int oldTop = -child.getHeight();\n                anim = getOffsetAnimator(child, oldTop, newTop);\n            } else if (mAfterVisible.contains(id)) {\n                addOccurTop = -1;\n                int oldTop = getHeight();\n                anim = getOffsetAnimator(child, oldTop, newTop);\n            } else {\n                // new add item\n                if (addOccurTop == -1) {\n                    addOccurTop = newTop;\n                    addOccurPosition = position;\n                }\n                anim = getAddAnimator(child, newTop, position, addOccurTop, addOccurPosition);\n            }\n            if (anim != null) {\n                animatorSet.play(anim);\n            }\n        }\n\n        if (mOpenChangeDisappearAnimation && mDetachViewsMap.size() > 0) {\n            mChangeDisappearAnimator = ValueAnimator.ofFloat(0, 1);\n            mChangeDisappearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    mChangeDisappearPlayTime = animation.getCurrentPlayTime();\n                    invalidate();\n                }\n            });\n            mChangeDisappearAnimator.addListener(new AnimatorListenerAdapter() {\n                @Override\n                public void onAnimationEnd(Animator animation) {\n                    super.onAnimationEnd(animation);\n                    mChangeDisappearPlayTime = 0;\n                }\n            });\n            mChangeDisappearAnimator.setDuration(getChangeDisappearDuration());\n            mChangeDisappearAnimator.start();\n        }\n        animatorSet.addListener(new ManipulateAnimatorListener() {\n            @Override\n            public void onAnimationEnd(final Animator animation) {\n                finishAnimation();\n            }\n        });\n\n        animatorSet.start();\n    }\n\n    protected long getChangeDisappearDuration() {\n        return (long) (getHeight() * mOffsetDurationUnit);\n    }\n\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        if (mOpenChangeDisappearAnimation && mChangeDisappearAnimator != null &&\n                mChangeDisappearAnimator.isStarted() && mDetachViewsMap.size() > 0 && mIsAnimating) {\n            for (int i = 0; i < mDetachViewsMap.size(); i++) {\n                long id = mDetachViewsMap.keyAt(i);\n                View view = mDetachViewsMap.valueAt(i);\n                int newPos = getPositionForId(id);\n                int top, offset = (int) (mChangeDisappearPlayTime / mOffsetDurationUnit);\n                if (newPos < getFirstVisiblePosition()) {\n                    top = mTopMap.get(id) - offset;\n                    if (top < -view.getHeight()) {\n                        continue;\n                    }\n                } else {\n                    top = mTopMap.get(id) + offset;\n                    if (top > getHeight()) {\n                        continue;\n                    }\n                }\n                view.layout(0, top, view.getWidth(), top + view.getHeight());\n                view.setAlpha(1f - mChangeDisappearPlayTime * 1f / getChangeDisappearDuration());\n                // 不能直接调用view.draw(canvas), 在listview上由于缓存会冲突\n                drawChild(canvas, view, getDrawingTime());\n            }\n        }\n    }\n\n    private void finishAnimation() {\n        mWrapperAdapter.setShouldNotifyChange(true);\n        mChangeDisappearAnimator = null;\n        if (mOpenChangeDisappearAnimation) {\n            for (int i = 0; i < mDetachViewsMap.size(); i++) {\n                mDetachViewsMap.valueAt(i).setAlpha(1);\n            }\n            mDetachViewsMap.clear();\n        }\n        mIsAnimating = false;\n        setEnabled(true);\n        setClickable(true);\n\n        manipulatePending();\n    }\n\n    private void manipulatePending() {\n\n        if (!mPendingManipulationsWithoutAnimation.isEmpty()) {\n            mIsAnimating = true;\n            for (final Manipulator manipulator : mPendingManipulationsWithoutAnimation) {\n                manipulator.manipulate(mRealAdapter);\n            }\n            mPendingManipulationsWithoutAnimation.clear();\n            mWrapperAdapter.notifyDataSetChanged();\n\n            post(new Runnable() {\n\n                @Override\n                public void run() {\n                    mIsAnimating = false;\n                    manipulatePending();\n                }\n            });\n        } else {\n\n            if (mPendingManipulations.isEmpty()) {\n                return;\n            }\n            mIsAnimating = true;\n            prepareAnimation();\n\n            for (final Manipulator manipulator : mPendingManipulations) {\n                manipulator.manipulate(mRealAdapter);\n            }\n            mPendingManipulations.clear();\n\n            doAnimation();\n        }\n    }\n\n    protected Animator getDeleteAnimator(View view) {\n        return alphaObjectAnimator(view, false, DURATION_ALPHA, true);\n    }\n\n    protected Animator getOffsetAnimator(View view, int oldTop, int newTop) {\n        return getOffsetAnimator(view, oldTop, newTop, getOffsetDuration(oldTop, newTop));\n    }\n\n    protected Animator getOffsetAnimator(View view, int oldTop, int newTop, long duration) {\n        final ObjectAnimator anim = ObjectAnimator.ofFloat(view,\n                \"translationY\", oldTop - newTop, 0);\n\n        anim.setDuration(duration);\n        anim.setInterpolator(mOffsetInterpolator);\n        return anim;\n    }\n\n    protected Animator getAddAnimator(View view, int top, int position, int addOccurTop, int addOccurPosition) {\n        view.setAlpha(0);\n        view.clearAnimation();\n        AnimatorSet animatorSet = new AnimatorSet();\n        animatorSet.play(alphaObjectAnimator(view, true, 50, false));\n        if (addOccurTop != top) {\n            animatorSet.play(getOffsetAnimator(view, addOccurTop, top));\n        }\n        animatorSet.setStartDelay((long) (view.getHeight() * mOffsetDurationUnit));\n        return animatorSet;\n    }\n\n    protected ObjectAnimator alphaObjectAnimator(View view, final boolean fadeIn, long duration, boolean postBack) {\n        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, \"alpha\", fadeIn ? new float[]{\n                0f, 1f} : new float[]{1f, 0f});\n\n        anim.setDuration(duration);\n\n        if (postBack) {\n            final WeakReference<View> wr = new WeakReference<>(view);\n            anim.addListener(new ManipulateAnimatorListener() {\n                @Override\n                public void onAnimationEnd(Animator animation) {\n                    if (wr.get() != null) {\n                        wr.get().setAlpha(fadeIn ? 0 : 1);\n                    }\n                }\n            });\n        }\n\n        return anim;\n    }\n\n    protected int getPositionForId(final long id) {\n        for (int i = 0; i < mWrapperAdapter.getCount(); i++) {\n            if (mWrapperAdapter.getItemId(i) == id) {\n                return i;\n            }\n        }\n\n        return -1;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        return isEnabled() && super.dispatchTouchEvent(ev);\n    }\n\n    public interface Manipulator<T extends ListAdapter> {\n        void manipulate(T adapter);\n    }\n\n    private static class WrapperAdapter extends BaseAdapter {\n        private ListAdapter mAdapter;\n        private boolean mShouldNotifyChange = true;\n        private final DataSetObserver mObserver = new DataSetObserver() {\n            @Override\n            public void onChanged() {\n                if (mShouldNotifyChange) {\n                    notifyDataSetChanged();\n                }\n            }\n\n            @Override\n            public void onInvalidated() {\n                notifyDataSetInvalidated();\n            }\n        };\n        private boolean mIsAnimationEnabled = false;\n\n        public WrapperAdapter(ListAdapter adapter) {\n            mAdapter = adapter;\n            mAdapter.registerDataSetObserver(mObserver);\n        }\n\n        public void setShouldNotifyChange(boolean shouldNotifyChange) {\n            mShouldNotifyChange = shouldNotifyChange;\n        }\n\n        public boolean isAnimationEnabled() {\n            return mIsAnimationEnabled;\n        }\n\n        @Override\n        public void notifyDataSetChanged() {\n            if (Looper.myLooper() != Looper.getMainLooper()) {\n                QMUILog.d(TAG, \"notifyDataSetChanged not in main Thread\");\n                return;\n            }\n            super.notifyDataSetChanged();\n        }\n\n        @Override\n        public int getCount() {\n            return mAdapter.getCount();\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return mAdapter.getItemViewType(position);\n        }\n\n        @Override\n        public int getViewTypeCount() {\n            return mAdapter.getViewTypeCount();\n        }\n\n        @Override\n        public Object getItem(int position) {\n            return mAdapter.getItem(position);\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return mAdapter.getItemId(position);\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            return mAdapter.getView(position, convertView, parent);\n        }\n\n        @Override\n        public boolean hasStableIds() {\n            boolean stable = mAdapter.hasStableIds();\n            mIsAnimationEnabled = stable;\n            return stable;\n        }\n    }\n\n    private abstract class ManipulateAnimatorListener implements Animator.AnimatorListener {\n\n        @Override\n        public void onAnimationStart(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationCancel(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationRepeat(Animator animation) {\n\n        }\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIAppBarLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport com.google.android.material.appbar.AppBarLayout;\n\n@Deprecated\npublic class QMUIAppBarLayout extends AppBarLayout {\n\n    public QMUIAppBarLayout(Context context) {\n        super(context);\n    }\n\n    public QMUIAppBarLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUICollapsingTopBarLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2015 The Android Open Source Project\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.qmuiteam.qmui.widget;\n\nimport android.animation.ValueAnimator;\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.WindowInsets;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.RestrictTo;\nimport androidx.annotation.StyleRes;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.graphics.Insets;\nimport androidx.core.graphics.drawable.DrawableCompat;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.google.android.material.appbar.AppBarLayout;\nimport com.google.android.material.appbar.CollapsingToolbarLayout;\nimport com.qmuiteam.qmui.QMUIInterpolatorStaticHolder;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.IQMUISkinDispatchInterceptor;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.util.QMUICollapsingTextHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.Objects;\n\nimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;\n\n/**\n * 参考 {@link CollapsingToolbarLayout}, 适配 QMUITopBar\n *\n * @author cginechen\n * @date 2017-09-02\n */\n\npublic class QMUICollapsingTopBarLayout extends FrameLayout implements IQMUISkinDispatchInterceptor {\n\n    private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600;\n\n    private boolean mRefreshToolbar = true;\n    private int mTopBarId;\n    private QMUITopBar mTopBar;\n    private View mTopBarDirectChild;\n\n    private int mExpandedMarginStart;\n    private int mExpandedMarginTop;\n    private int mExpandedMarginEnd;\n    private int mExpandedMarginBottom;\n\n    private final Rect mTmpRect = new Rect();\n    final QMUICollapsingTextHelper mCollapsingTextHelper;\n    private boolean mCollapsingTitleEnabled;\n\n    private Drawable mContentScrim;\n    Drawable mStatusBarScrim;\n    private int mScrimAlpha;\n    private boolean mScrimsAreShown;\n    private ValueAnimator mScrimAnimator;\n    private long mScrimAnimationDuration;\n    private int mScrimVisibleHeightTrigger = -1;\n\n    private AppBarLayout.OnOffsetChangedListener mOnOffsetChangedListener;\n    private ValueAnimator.AnimatorUpdateListener mScrimUpdateListener;\n    private ArrayList<OnOffsetUpdateListener> mOnOffsetUpdateListeners = new ArrayList<>();\n\n    int mCurrentOffset;\n\n    Insets mLastInsets;\n\n    private int mContentScrimSkinAttr = 0;\n    private int mStatusBarScrimSkinAttr = 0;\n    private int mCollapsedTextColorSkinAttr = 0;\n    private int mExpandedTextColorSkinAttr = 0;\n\n    public QMUICollapsingTopBarLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUICollapsingTopBarLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUICollapsingTopBarLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        mCollapsingTextHelper = new QMUICollapsingTextHelper(this);\n        mCollapsingTextHelper.setTextSizeInterpolator(QMUIInterpolatorStaticHolder.DECELERATE_INTERPOLATOR);\n\n        QMUIViewHelper.checkAppCompatTheme(context);\n\n        TypedArray a = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUICollapsingTopBarLayout, defStyleAttr, 0);\n\n        mCollapsingTextHelper.setExpandedTextGravity(\n                a.getInt(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleGravity,\n                        Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));\n        mCollapsingTextHelper.setCollapsedTextGravity(\n                a.getInt(R.styleable.QMUICollapsingTopBarLayout_qmui_collapsedTitleGravity,\n                        GravityCompat.START | Gravity.CENTER_VERTICAL));\n\n\n        mExpandedMarginStart = mExpandedMarginTop = mExpandedMarginEnd = mExpandedMarginBottom =\n                a.getDimensionPixelSize(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMargin, 0);\n\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginStart)) {\n            mExpandedMarginStart = a.getDimensionPixelSize(\n                    R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginStart, 0);\n        }\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginEnd)) {\n            mExpandedMarginEnd = a.getDimensionPixelSize(\n                    R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginEnd, 0);\n        }\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginTop)) {\n            mExpandedMarginTop = a.getDimensionPixelSize(\n                    R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginTop, 0);\n        }\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginBottom)) {\n            mExpandedMarginBottom = a.getDimensionPixelSize(\n                    R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleMarginBottom, 0);\n        }\n\n        mCollapsingTitleEnabled = a.getBoolean(R.styleable.QMUICollapsingTopBarLayout_qmui_titleEnabled, true);\n        setTitle(a.getText(R.styleable.QMUICollapsingTopBarLayout_qmui_title));\n\n        // First load the default text appearances\n        mCollapsingTextHelper.setExpandedTextAppearance(R.style.QMUI_CollapsingTopBarLayoutExpanded);\n        mCollapsingTextHelper.setCollapsedTextAppearance(R.style.QMUI_CollapsingTopBarLayoutCollapsed);\n\n        // Now overlay any custom text appearances\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleTextAppearance)) {\n            mCollapsingTextHelper.setExpandedTextAppearance(\n                    a.getResourceId(R.styleable.QMUICollapsingTopBarLayout_qmui_expandedTitleTextAppearance, 0));\n        }\n        if (a.hasValue(R.styleable.QMUICollapsingTopBarLayout_qmui_collapsedTitleTextAppearance)) {\n            mCollapsingTextHelper.setCollapsedTextAppearance(\n                    a.getResourceId(R.styleable.QMUICollapsingTopBarLayout_qmui_collapsedTitleTextAppearance, 0));\n        }\n\n        mScrimVisibleHeightTrigger = a.getDimensionPixelSize(\n                R.styleable.QMUICollapsingTopBarLayout_qmui_scrimVisibleHeightTrigger, -1);\n\n        mScrimAnimationDuration = a.getInt(\n                R.styleable.QMUICollapsingTopBarLayout_qmui_scrimAnimationDuration,\n                DEFAULT_SCRIM_ANIMATION_DURATION);\n\n\n        mTopBarId = a.getResourceId(R.styleable.QMUICollapsingTopBarLayout_qmui_topBarId, -1);\n\n        if (a.getBoolean(R.styleable.QMUICollapsingTopBarLayout_qmui_followTopBarCommonSkin, false)) {\n            followTopBarCommonSkin();\n        } else {\n            setContentScrimInner(a.getDrawable(R.styleable.QMUICollapsingTopBarLayout_qmui_contentScrim));\n            setStatusBarScrimInner(a.getDrawable(R.styleable.QMUICollapsingTopBarLayout_qmui_statusBarScrim));\n        }\n        a.recycle();\n\n        setWillNotDraw(false);\n\n        QMUIWindowInsetHelper.handleWindowInsets(this,\n                WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout(),\n                new QMUIWindowInsetHelper.InsetHandler() {\n                    @Override\n                    public void handleInset(View view, Insets insets) {\n                        Insets newInsets = null;\n                        if (ViewCompat.getFitsSystemWindows(view)) {\n                            // If we're set to fit system windows, keep the insets\n                            newInsets = insets;\n                        }\n\n                        // If our insets have changed, keep them and invalidate the scroll ranges...\n                        if (!Objects.equals(mLastInsets, insets)) {\n                            mLastInsets = newInsets;\n                            requestLayout();\n                        }\n                    }\n                },\n                true,\n                false,\n                true\n        );\n    }\n\n    public void followTopBarCommonSkin() {\n        setCollapsedTextColorSkinAttr(R.attr.qmui_skin_support_topbar_title_color);\n        setExpandedTextColorSkinAttr(R.attr.qmui_skin_support_topbar_title_color);\n        setContentScrimSkinAttr(R.attr.qmui_skin_support_topbar_bg);\n        setStatusBarScrimSkinAttr(R.attr.qmui_skin_support_topbar_bg);\n    }\n\n    @Override\n    public void onViewAdded(View child) {\n        super.onViewAdded(child);\n        if (child instanceof QMUITopBar) {\n            ((QMUITopBar) child).disableBackgroundSetter();\n        }\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        // Add an OnOffsetChangedListener if possible\n        final ViewParent parent = getParent();\n        if (parent instanceof AppBarLayout) {\n            // Copy over from the ABL whether we should fit system windows\n            ViewCompat.setFitsSystemWindows(this, ViewCompat.getFitsSystemWindows((View) parent));\n\n            if (mOnOffsetChangedListener == null) {\n                mOnOffsetChangedListener = new OffsetUpdateListener();\n            }\n            ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener);\n\n            // We're attached, so lets request an inset dispatch\n            ViewCompat.requestApplyInsets(this);\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        // Remove our OnOffsetChangedListener if possible and it exists\n        final ViewParent parent = getParent();\n        if (mOnOffsetChangedListener != null && parent instanceof AppBarLayout) {\n            ((AppBarLayout) parent).removeOnOffsetChangedListener(mOnOffsetChangedListener);\n        }\n\n        super.onDetachedFromWindow();\n    }\n\n\n    @Override\n    public void draw(Canvas canvas) {\n        super.draw(canvas);\n\n        // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.\n        // Instead, we draw it here, before our collapsing text.\n        ensureToolbar();\n        if (mTopBar == null && mContentScrim != null && mScrimAlpha > 0) {\n            mContentScrim.mutate().setAlpha(mScrimAlpha);\n            mContentScrim.draw(canvas);\n        }\n\n        // Let the collapsing text helper draw its text\n        if (mCollapsingTitleEnabled) {\n            mCollapsingTextHelper.draw(canvas);\n        }\n\n        // Now draw the status bar scrim\n        if (mStatusBarScrim != null && mScrimAlpha > 0) {\n            final int topInset = getWindowInsetTop();\n            if (topInset > 0) {\n                mStatusBarScrim.setBounds(0, -mCurrentOffset, getWidth(),\n                        topInset - mCurrentOffset);\n                mStatusBarScrim.mutate().setAlpha(mScrimAlpha);\n                mStatusBarScrim.draw(canvas);\n            }\n        }\n    }\n\n    private int getWindowInsetTop() {\n        if (mLastInsets != null) {\n            return mLastInsets.top;\n        }\n        return 0;\n    }\n\n    @Override\n    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {\n        // This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),\n        // but in front of any other children which are behind it. To do this we intercept the\n        // drawChild() call, and draw our scrim just before the Toolbar is drawn\n        boolean invalidated = false;\n        if (mContentScrim != null && mScrimAlpha > 0 && isToolbarChild(child)) {\n            mContentScrim.mutate().setAlpha(mScrimAlpha);\n            mContentScrim.draw(canvas);\n            invalidated = true;\n        }\n        return super.drawChild(canvas, child, drawingTime) || invalidated;\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        if (mContentScrim != null) {\n            mContentScrim.setBounds(0, 0, w, h);\n        }\n    }\n\n    private void ensureToolbar() {\n        if (!mRefreshToolbar) {\n            return;\n        }\n\n        // First clear out the current Toolbar\n        mTopBar = null;\n        mTopBarDirectChild = null;\n\n        if (mTopBarId != -1) {\n            // If we have an ID set, try and find it and it's direct parent to us\n            mTopBar = (QMUITopBar) findViewById(mTopBarId);\n            if (mTopBar != null) {\n                mTopBarDirectChild = findDirectChild(mTopBar);\n            }\n        }\n\n        if (mTopBar == null) {\n            // If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find\n            // one from our direct children\n            QMUITopBar topBar = null;\n            for (int i = 0, count = getChildCount(); i < count; i++) {\n                final View child = getChildAt(i);\n                if (child instanceof QMUITopBar) {\n                    topBar = (QMUITopBar) child;\n                    break;\n                }\n            }\n            mTopBar = topBar;\n        }\n        mRefreshToolbar = false;\n    }\n\n    private boolean isToolbarChild(View child) {\n        return (mTopBarDirectChild == null || mTopBarDirectChild == this)\n                ? child == mTopBar\n                : child == mTopBarDirectChild;\n    }\n\n    /**\n     * Returns the direct child of this layout, which itself is the ancestor of the\n     * given view.\n     */\n    private View findDirectChild(final View descendant) {\n        View directChild = descendant;\n        for (ViewParent p = descendant.getParent(); p != this && p != null; p = p.getParent()) {\n            if (p instanceof View) {\n                directChild = (View) p;\n            }\n        }\n        return directChild;\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        ensureToolbar();\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n    @Override\n    public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {\n        super.dispatchApplyWindowInsets(insets);\n        // stop dispatch, but prevent stop parent sibling.\n        return insets;\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (mLastInsets != null) {\n            // Shift down any views which are not set to fit system windows\n            final int insetTop = getWindowInsetTop();\n            for (int i = 0, z = getChildCount(); i < z; i++) {\n                final View child = getChildAt(i);\n                if (ViewCompat.getFitsSystemWindows(child)) {\n                    if (child.getTop() < insetTop) {\n                        // If the child isn't set to fit system windows but is drawing within\n                        // the inset offset it down\n                        ViewCompat.offsetTopAndBottom(child, insetTop);\n                    }\n                }\n            }\n        }\n\n        for (int i = 0, z = getChildCount(); i < z; i++) {\n            getViewOffsetHelper(getChildAt(i)).onViewLayout(false);\n        }\n\n        // Update the collapsed bounds by getting it's transformed bounds\n        if (mCollapsingTitleEnabled) {\n            // Update the collapsed bounds\n            final int maxOffset = getMaxOffsetForPinChild(\n                    mTopBarDirectChild != null ? mTopBarDirectChild : mTopBar);\n            QMUIViewHelper.getDescendantRect(this, mTopBar, mTmpRect);\n//            mTmpRect.top = mTmpRect.top - topBarInsetAdjustTop;\n            Rect rect = mTopBar.getTitleContainerRect();\n            mCollapsingTextHelper.setCollapsedBounds(\n                    mTmpRect.left + rect.left,\n                    mTmpRect.top + maxOffset + rect.top,\n                    mTmpRect.left + rect.right,\n                    mTmpRect.top + maxOffset + rect.bottom);\n\n            // Update the expanded bounds\n            mCollapsingTextHelper.setExpandedBounds(\n                    mExpandedMarginStart,\n                    mTmpRect.top + mExpandedMarginTop,\n                    right - left - mExpandedMarginEnd,\n                    bottom - top - mExpandedMarginBottom);\n            // Now recalculate using the new bounds\n            mCollapsingTextHelper.recalculate();\n        }\n\n\n        // Finally, set our minimum height to enable proper AppBarLayout collapsing\n        if (mTopBar != null) {\n            if (mCollapsingTitleEnabled && TextUtils.isEmpty(mCollapsingTextHelper.getText())) {\n                // If we do not currently have a title, try and grab it from the Toolbar\n                mCollapsingTextHelper.setText(mTopBar.getTitle());\n            }\n            if (mTopBarDirectChild == null || mTopBarDirectChild == this) {\n                setMinimumHeight(getHeightWithMargins(mTopBar));\n            } else {\n                setMinimumHeight(getHeightWithMargins(mTopBarDirectChild));\n            }\n        }\n\n        updateScrimVisibility();\n\n        for (int i = 0, z = getChildCount(); i < z; i++) {\n            getViewOffsetHelper(getChildAt(i)).applyOffsets();\n        }\n    }\n\n    private static int getHeightWithMargins(@NonNull final View view) {\n        final ViewGroup.LayoutParams lp = view.getLayoutParams();\n        if (lp instanceof MarginLayoutParams) {\n            final MarginLayoutParams mlp = (MarginLayoutParams) lp;\n            return view.getHeight() + mlp.topMargin + mlp.bottomMargin;\n        }\n        return view.getHeight();\n    }\n\n    static QMUIViewOffsetHelper getViewOffsetHelper(View view) {\n        QMUIViewOffsetHelper offsetHelper = (QMUIViewOffsetHelper) view.getTag(R.id.qmui_view_offset_helper);\n        if (offsetHelper == null) {\n            offsetHelper = new QMUIViewOffsetHelper(view);\n            view.setTag(R.id.qmui_view_offset_helper, offsetHelper);\n        }\n        return offsetHelper;\n    }\n\n    /**\n     * Sets the title to be displayed by this view, if enabled.\n     *\n     * @see #setTitleEnabled(boolean)\n     * @see #getTitle()\n     */\n    public void setTitle(@Nullable CharSequence title) {\n        mCollapsingTextHelper.setText(title);\n    }\n\n    /**\n     * Returns the title currently being displayed by this view. If the title is not enabled, then\n     * this will return {@code null}.\n     */\n    @Nullable\n    public CharSequence getTitle() {\n        return mCollapsingTitleEnabled ? mCollapsingTextHelper.getText() : null;\n    }\n\n    /**\n     * Sets whether this view should display its own title.\n     * <p>\n     * <p>The title displayed by this view will shrink and grow based on the scroll offset.</p>\n     *\n     * @see #setTitle(CharSequence)\n     * @see #isTitleEnabled()\n     */\n    public void setTitleEnabled(boolean enabled) {\n        if (enabled != mCollapsingTitleEnabled) {\n            mCollapsingTitleEnabled = enabled;\n            requestLayout();\n        }\n    }\n\n    /**\n     * Returns whether this view is currently displaying its own title.\n     *\n     * @see #setTitleEnabled(boolean)\n     */\n    public boolean isTitleEnabled() {\n        return mCollapsingTitleEnabled;\n    }\n\n    /**\n     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change\n     * in the vertical scroll may overwrite this value. Any visibility change will be animated if\n     * this view has already been laid out.\n     *\n     * @param shown whether the scrims should be shown\n     * @see #getStatusBarScrim()\n     * @see #getContentScrim()\n     */\n    public void setScrimsShown(boolean shown) {\n        setScrimsShown(shown, ViewCompat.isLaidOut(this) && !isInEditMode());\n    }\n\n    /**\n     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change\n     * in the vertical scroll may overwrite this value.\n     *\n     * @param shown   whether the scrims should be shown\n     * @param animate whether to animate the visibility change\n     * @see #getStatusBarScrim()\n     * @see #getContentScrim()\n     */\n    public void setScrimsShown(boolean shown, boolean animate) {\n        if (mScrimsAreShown != shown) {\n            if (animate) {\n                animateScrim(shown ? 0xFF : 0x0);\n            } else {\n                setScrimAlpha(shown ? 0xFF : 0x0);\n            }\n            mScrimsAreShown = shown;\n        }\n    }\n\n    /**\n     * @param scrimUpdateListener 为 null 则是 removeUpdateListener\n     */\n    public void setScrimUpdateListener(ValueAnimator.AnimatorUpdateListener scrimUpdateListener) {\n        if (mScrimUpdateListener != scrimUpdateListener) {\n            if (mScrimAnimator == null) {\n                mScrimUpdateListener = scrimUpdateListener;\n            } else {\n                if (mScrimUpdateListener != null) {\n                    mScrimAnimator.removeUpdateListener(mScrimUpdateListener);\n                }\n                mScrimUpdateListener = scrimUpdateListener;\n                if (mScrimUpdateListener != null) {\n                    mScrimAnimator.addUpdateListener(mScrimUpdateListener);\n                }\n            }\n        }\n    }\n\n    private void animateScrim(int targetAlpha) {\n        ensureToolbar();\n        if (mScrimAnimator == null) {\n            mScrimAnimator = new ValueAnimator();\n            mScrimAnimator.setDuration(mScrimAnimationDuration);\n            mScrimAnimator.setInterpolator(\n                    targetAlpha > mScrimAlpha\n                            ? QMUIInterpolatorStaticHolder.FAST_OUT_LINEAR_IN_INTERPOLATOR\n                            : QMUIInterpolatorStaticHolder.LINEAR_OUT_SLOW_IN_INTERPOLATOR);\n            mScrimAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animator) {\n                    setScrimAlpha((Integer) animator.getAnimatedValue());\n                }\n            });\n            if (mScrimUpdateListener != null) {\n                mScrimAnimator.addUpdateListener(mScrimUpdateListener);\n            }\n        } else if (mScrimAnimator.isRunning()) {\n            mScrimAnimator.cancel();\n        }\n\n        mScrimAnimator.setIntValues(mScrimAlpha, targetAlpha);\n        mScrimAnimator.start();\n    }\n\n    void setScrimAlpha(int alpha) {\n        if (alpha != mScrimAlpha) {\n            final Drawable contentScrim = mContentScrim;\n            if (contentScrim != null && mTopBar != null) {\n                ViewCompat.postInvalidateOnAnimation(mTopBar);\n            }\n            mScrimAlpha = alpha;\n            ViewCompat.postInvalidateOnAnimation(QMUICollapsingTopBarLayout.this);\n        }\n    }\n\n    int getScrimAlpha() {\n        return mScrimAlpha;\n    }\n\n    public void setContentScrimSkinAttr(int contentScrimSkinAttr) {\n        mContentScrimSkinAttr = contentScrimSkinAttr;\n        if (contentScrimSkinAttr != 0) {\n            setStatusBarScrimInner(QMUISkinHelper.getSkinDrawable(this, contentScrimSkinAttr));\n        }\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources. Providing null will disable\n     * the scrim functionality.\n     *\n     * @param drawable the drawable to display\n     * @see #getContentScrim()\n     */\n    public void setContentScrim(@Nullable Drawable drawable) {\n        mContentScrimSkinAttr = 0;\n        setContentScrimInner(drawable);\n    }\n\n    private void setContentScrimInner(@Nullable Drawable drawable) {\n        if (mContentScrim != drawable) {\n            if (mContentScrim != null) {\n                mContentScrim.setCallback(null);\n            }\n            mContentScrim = drawable != null ? drawable.mutate() : null;\n            if (mContentScrim != null) {\n                mContentScrim.setBounds(0, 0, getWidth(), getHeight());\n                mContentScrim.setCallback(this);\n                mContentScrim.setAlpha(mScrimAlpha);\n            }\n            ViewCompat.postInvalidateOnAnimation(this);\n        }\n    }\n\n    /**\n     * Set the color to use for the content scrim.\n     *\n     * @param color the color to display\n     * @see #getContentScrim()\n     */\n    public void setContentScrimColor(@ColorInt int color) {\n        setContentScrim(new ColorDrawable(color));\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources.\n     *\n     * @param resId drawable resource id\n     * @see #getContentScrim()\n     */\n    public void setContentScrimResource(@DrawableRes int resId) {\n        setContentScrim(ContextCompat.getDrawable(getContext(), resId));\n\n    }\n\n    /**\n     * Returns the drawable which is used for the foreground scrim.\n     *\n     * @see #setContentScrim(Drawable)\n     */\n    @Nullable\n    public Drawable getContentScrim() {\n        return mContentScrim;\n    }\n\n    /**\n     * Set the drawable to use for the status bar scrim from resources.\n     * Providing null will disable the scrim functionality.\n     * <p>\n     * <p>This scrim is only shown when we have been given a top system inset.</p>\n     *\n     * @param drawable the drawable to display\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrim(@Nullable Drawable drawable) {\n        mStatusBarScrimSkinAttr = 0;\n        setStatusBarScrimInner(drawable);\n    }\n\n    private void setStatusBarScrimInner(@Nullable Drawable drawable) {\n        if (mStatusBarScrim != drawable) {\n            if (mStatusBarScrim != null) {\n                mStatusBarScrim.setCallback(null);\n            }\n            mStatusBarScrim = drawable != null ? drawable.mutate() : null;\n            if (mStatusBarScrim != null) {\n                if (mStatusBarScrim.isStateful()) {\n                    mStatusBarScrim.setState(getDrawableState());\n                }\n                DrawableCompat.setLayoutDirection(mStatusBarScrim,\n                        ViewCompat.getLayoutDirection(this));\n                mStatusBarScrim.setVisible(getVisibility() == VISIBLE, false);\n                mStatusBarScrim.setCallback(this);\n                mStatusBarScrim.setAlpha(mScrimAlpha);\n            }\n            ViewCompat.postInvalidateOnAnimation(this);\n        }\n    }\n\n    public void setStatusBarScrimSkinAttr(int statusBarScrimSkinAttr) {\n        mStatusBarScrimSkinAttr = statusBarScrimSkinAttr;\n        if (mStatusBarScrimSkinAttr != 0) {\n            setStatusBarScrimInner(QMUISkinHelper.getSkinDrawable(this, statusBarScrimSkinAttr));\n        }\n    }\n\n    // 从系统源码获取，不作检测\n    @SuppressWarnings(\"ConstantConditions\")\n    @Override\n    protected void drawableStateChanged() {\n        super.drawableStateChanged();\n\n        final int[] state = getDrawableState();\n        boolean changed = false;\n\n        Drawable d = mStatusBarScrim;\n        if (d != null && d.isStateful()) {\n            changed |= d.setState(state);\n        }\n        d = mContentScrim;\n        if (d != null && d.isStateful()) {\n            changed |= d.setState(state);\n        }\n        if (mCollapsingTextHelper != null) {\n            changed |= mCollapsingTextHelper.setState(state);\n        }\n\n        if (changed) {\n            invalidate();\n        }\n    }\n\n    @Override\n    protected boolean verifyDrawable(@NonNull Drawable who) {\n        return super.verifyDrawable(who) || who == mContentScrim || who == mStatusBarScrim;\n    }\n\n    @Override\n    public void setVisibility(int visibility) {\n        super.setVisibility(visibility);\n\n        final boolean visible = visibility == VISIBLE;\n        if (mStatusBarScrim != null && mStatusBarScrim.isVisible() != visible) {\n            mStatusBarScrim.setVisible(visible, false);\n        }\n        if (mContentScrim != null && mContentScrim.isVisible() != visible) {\n            mContentScrim.setVisible(visible, false);\n        }\n    }\n\n    /**\n     * Set the color to use for the status bar scrim.\n     * <p>\n     * <p>This scrim is only shown when we have been given a top system inset.</p>\n     *\n     * @param color the color to display\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrimColor(@ColorInt int color) {\n        setStatusBarScrim(new ColorDrawable(color));\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources.\n     *\n     * @param resId drawable resource id\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrimResource(@DrawableRes int resId) {\n        setStatusBarScrim(ContextCompat.getDrawable(getContext(), resId));\n    }\n\n    /**\n     * Returns the drawable which is used for the status bar scrim.\n     *\n     * @see #setStatusBarScrim(Drawable)\n     */\n    @Nullable\n    public Drawable getStatusBarScrim() {\n        return mStatusBarScrim;\n    }\n\n    /**\n     * Sets the text color and size for the collapsed title from the specified\n     * TextAppearance resource.\n     */\n    public void setCollapsedTitleTextAppearance(@StyleRes int resId) {\n        mCollapsingTextHelper.setCollapsedTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the collapsed title.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setCollapsedTitleTextColor(@ColorInt int color) {\n        setCollapsedTitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the collapsed title.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) {\n        mCollapsedTextColorSkinAttr = 0;\n        mCollapsingTextHelper.setCollapsedTextColor(colors);\n    }\n\n    public void setCollapsedTextColorSkinAttr(int attr) {\n        mCollapsedTextColorSkinAttr = attr;\n        if (attr != 0) {\n            mCollapsingTextHelper.setCollapsedTextColor(\n                    QMUISkinHelper.getSkinColorStateList(this, attr));\n        }\n    }\n\n    /**\n     * Sets the horizontal alignment of the collapsed title and the vertical gravity that will\n     * be used when there is extra space in the collapsed bounds beyond what is required for\n     * the title itself.\n     */\n    public void setCollapsedTitleGravity(int gravity) {\n        mCollapsingTextHelper.setCollapsedTextGravity(gravity);\n    }\n\n    /**\n     * Returns the horizontal and vertical alignment for title when collapsed.\n     */\n    public int getCollapsedTitleGravity() {\n        return mCollapsingTextHelper.getCollapsedTextGravity();\n    }\n\n    /**\n     * Sets the text color and size for the expanded title from the specified\n     * TextAppearance resource.\n     */\n    public void setExpandedTitleTextAppearance(@StyleRes int resId) {\n        mCollapsingTextHelper.setExpandedTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the expanded title.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setExpandedTitleColor(@ColorInt int color) {\n        setExpandedTitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the expanded title.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setExpandedTitleTextColor(@NonNull ColorStateList colors) {\n        mExpandedTextColorSkinAttr = 0;\n        mCollapsingTextHelper.setExpandedTextColor(colors);\n    }\n\n    public void setExpandedTextColorSkinAttr(int attr) {\n        mExpandedTextColorSkinAttr = attr;\n        if (attr != 0) {\n            mCollapsingTextHelper.setExpandedTextColor(\n                    QMUISkinHelper.getSkinColorStateList(this, attr));\n        }\n    }\n\n    /**\n     * Sets the horizontal alignment of the expanded title and the vertical gravity that will\n     * be used when there is extra space in the expanded bounds beyond what is required for\n     * the title itself.\n     */\n    public void setExpandedTitleGravity(int gravity) {\n        mCollapsingTextHelper.setExpandedTextGravity(gravity);\n    }\n\n    /**\n     * Returns the horizontal and vertical alignment for title when expanded.\n     */\n    public int getExpandedTitleGravity() {\n        return mCollapsingTextHelper.getExpandedTextGravity();\n    }\n\n    /**\n     * Set the typeface to use for the collapsed title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setCollapsedTitleTypeface(@Nullable Typeface typeface) {\n        mCollapsingTextHelper.setCollapsedTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the collapsed title.\n     */\n    @NonNull\n    public Typeface getCollapsedTitleTypeface() {\n        return mCollapsingTextHelper.getCollapsedTypeface();\n    }\n\n    /**\n     * Set the typeface to use for the expanded title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setExpandedTitleTypeface(@Nullable Typeface typeface) {\n        mCollapsingTextHelper.setExpandedTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the expanded title.\n     */\n    @NonNull\n    public Typeface getExpandedTitleTypeface() {\n        return mCollapsingTextHelper.getExpandedTypeface();\n    }\n\n    /**\n     * Sets the expanded title margins.\n     *\n     * @param start  the starting title margin in pixels\n     * @param top    the top title margin in pixels\n     * @param end    the ending title margin in pixels\n     * @param bottom the bottom title margin in pixels\n     * @see #getExpandedTitleMarginStart()\n     * @see #getExpandedTitleMarginTop()\n     * @see #getExpandedTitleMarginEnd()\n     * @see #getExpandedTitleMarginBottom()\n     */\n    public void setExpandedTitleMargin(int start, int top, int end, int bottom) {\n        mExpandedMarginStart = start;\n        mExpandedMarginTop = top;\n        mExpandedMarginEnd = end;\n        mExpandedMarginBottom = bottom;\n        requestLayout();\n    }\n\n    /**\n     * @return the starting expanded title margin in pixels\n     * @see #setExpandedTitleMarginStart(int)\n     */\n    public int getExpandedTitleMarginStart() {\n        return mExpandedMarginStart;\n    }\n\n    /**\n     * Sets the starting expanded title margin in pixels.\n     *\n     * @param margin the starting title margin in pixels\n     * @see #getExpandedTitleMarginStart()\n     */\n    public void setExpandedTitleMarginStart(int margin) {\n        mExpandedMarginStart = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the top expanded title margin in pixels\n     * @see #setExpandedTitleMarginTop(int)\n     */\n    public int getExpandedTitleMarginTop() {\n        return mExpandedMarginTop;\n    }\n\n    /**\n     * Sets the top expanded title margin in pixels.\n     *\n     * @param margin the top title margin in pixels\n     * @see #getExpandedTitleMarginTop()\n     */\n    public void setExpandedTitleMarginTop(int margin) {\n        mExpandedMarginTop = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the ending expanded title margin in pixels\n     * @see #setExpandedTitleMarginEnd(int)\n     */\n    public int getExpandedTitleMarginEnd() {\n        return mExpandedMarginEnd;\n    }\n\n    /**\n     * Sets the ending expanded title margin in pixels.\n     *\n     * @param margin the ending title margin in pixels\n     * @see #getExpandedTitleMarginEnd()\n     */\n    public void setExpandedTitleMarginEnd(int margin) {\n        mExpandedMarginEnd = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the bottom expanded title margin in pixels\n     * @see #setExpandedTitleMarginBottom(int)\n     */\n    public int getExpandedTitleMarginBottom() {\n        return mExpandedMarginBottom;\n    }\n\n    /**\n     * Sets the bottom expanded title margin in pixels.\n     *\n     * @param margin the bottom title margin in pixels\n     * @see #getExpandedTitleMarginBottom()\n     */\n    public void setExpandedTitleMarginBottom(int margin) {\n        mExpandedMarginBottom = margin;\n        requestLayout();\n    }\n\n    /**\n     * Set the amount of visible height in pixels used to define when to trigger a scrim\n     * visibility change.\n     * <p>\n     * <p>If the visible height of this view is less than the given value, the scrims will be\n     * made visible, otherwise they are hidden.</p>\n     *\n     * @param height value in pixels used to define when to trigger a scrim visibility change\n     */\n    public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) {\n        if (mScrimVisibleHeightTrigger != height) {\n            mScrimVisibleHeightTrigger = height;\n            // Update the scrim visibility\n            updateScrimVisibility();\n        }\n    }\n\n    /**\n     * Returns the amount of visible height in pixels used to define when to trigger a scrim\n     * visibility change.\n     *\n     * @see #setScrimVisibleHeightTrigger(int)\n     */\n    public int getScrimVisibleHeightTrigger() {\n        if (mScrimVisibleHeightTrigger >= 0) {\n            // If we have one explicitly set, return it\n            return mScrimVisibleHeightTrigger;\n        }\n\n        // Otherwise we'll use the default computed value\n        final int insetTop = getWindowInsetTop();\n\n        final int minHeight = ViewCompat.getMinimumHeight(this);\n        if (minHeight > 0) {\n            // If we have a minHeight set, lets use 2 * minHeight (capped at our height)\n            return Math.min((minHeight * 2) + insetTop, getHeight());\n        }\n\n        // If we reach here then we don't have a min height set. Instead we'll take a\n        // guess at 1/3 of our height being visible\n        return getHeight() / 3;\n    }\n\n    /**\n     * Set the duration used for scrim visibility animations.\n     *\n     * @param duration the duration to use in milliseconds\n     */\n    public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) {\n        mScrimAnimationDuration = duration;\n    }\n\n    /**\n     * Returns the duration in milliseconds used for scrim visibility animations.\n     */\n    public long getScrimAnimationDuration() {\n        return mScrimAnimationDuration;\n    }\n\n    @Override\n    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n        return p instanceof QMUICollapsingTopBarLayout.LayoutParams;\n    }\n\n    @Override\n    protected QMUICollapsingTopBarLayout.LayoutParams generateDefaultLayoutParams() {\n        return new QMUICollapsingTopBarLayout.LayoutParams(QMUICollapsingTopBarLayout.LayoutParams.MATCH_PARENT, QMUICollapsingTopBarLayout.LayoutParams.MATCH_PARENT);\n    }\n\n    @Override\n    public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {\n        return new QMUICollapsingTopBarLayout.LayoutParams(getContext(), attrs);\n    }\n\n    @Override\n    protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\n        return new QMUICollapsingTopBarLayout.LayoutParams(p);\n    }\n\n\n    public static class LayoutParams extends FrameLayout.LayoutParams {\n\n        private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f;\n\n        @RestrictTo(LIBRARY_GROUP)\n        @IntDef({\n                COLLAPSE_MODE_OFF,\n                COLLAPSE_MODE_PIN,\n                COLLAPSE_MODE_PARALLAX\n        })\n        @Retention(RetentionPolicy.SOURCE)\n        public @interface CollapseMode {\n        }\n\n        /**\n         * The view will act as normal with no collapsing behavior.\n         */\n        public static final int COLLAPSE_MODE_OFF = 0;\n\n        /**\n         * The view will pin in place until it reaches the bottom of the\n         * {@link QMUICollapsingTopBarLayout}.\n         */\n        public static final int COLLAPSE_MODE_PIN = 1;\n\n        /**\n         * The view will scroll in a parallax fashion. See {@link #setParallaxMultiplier(float)}\n         * to change the multiplier used.\n         */\n        public static final int COLLAPSE_MODE_PARALLAX = 2;\n\n        int mCollapseMode = COLLAPSE_MODE_OFF;\n        float mParallaxMult = DEFAULT_PARALLAX_MULTIPLIER;\n\n        public LayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n\n            TypedArray a = c.obtainStyledAttributes(attrs,\n                    R.styleable.QMUICollapsingTopBarLayout_Layout);\n            mCollapseMode = a.getInt(\n                    R.styleable.QMUICollapsingTopBarLayout_Layout_qmui_layout_collapseMode,\n                    COLLAPSE_MODE_OFF);\n            setParallaxMultiplier(a.getFloat(\n                    R.styleable.QMUICollapsingTopBarLayout_Layout_qmui_layout_collapseParallaxMultiplier,\n                    DEFAULT_PARALLAX_MULTIPLIER));\n            a.recycle();\n        }\n\n        public LayoutParams(int width, int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(int width, int height, int gravity) {\n            super(width, height, gravity);\n        }\n\n        public LayoutParams(ViewGroup.LayoutParams p) {\n            super(p);\n        }\n\n        public LayoutParams(MarginLayoutParams source) {\n            super(source);\n        }\n\n        @RequiresApi(19)\n        @TargetApi(19)\n        public LayoutParams(FrameLayout.LayoutParams source) {\n            // The copy constructor called here only exists on API 19+.\n            super(source);\n        }\n\n        /**\n         * Set the collapse mode.\n         *\n         * @param collapseMode one of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}\n         *                     or {@link #COLLAPSE_MODE_PARALLAX}.\n         */\n        public void setCollapseMode(@CollapseMode int collapseMode) {\n            mCollapseMode = collapseMode;\n        }\n\n        /**\n         * Returns the requested collapse mode.\n         *\n         * @return the current mode. One of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}\n         * or {@link #COLLAPSE_MODE_PARALLAX}.\n         */\n        @CollapseMode\n        public int getCollapseMode() {\n            return mCollapseMode;\n        }\n\n        /**\n         * Set the parallax scroll multiplier used in conjunction with\n         * {@link #COLLAPSE_MODE_PARALLAX}. A value of {@code 0.0} indicates no movement at all,\n         * {@code 1.0f} indicates normal scroll movement.\n         *\n         * @param multiplier the multiplier.\n         * @see #getParallaxMultiplier()\n         */\n        public void setParallaxMultiplier(float multiplier) {\n            mParallaxMult = multiplier;\n        }\n\n        /**\n         * Returns the parallax scroll multiplier used in conjunction with\n         * {@link #COLLAPSE_MODE_PARALLAX}.\n         *\n         * @see #setParallaxMultiplier(float)\n         */\n        public float getParallaxMultiplier() {\n            return mParallaxMult;\n        }\n    }\n\n    /**\n     * Show or hide the scrims if needed\n     */\n    final void updateScrimVisibility() {\n        if (mContentScrim != null || mStatusBarScrim != null) {\n            setScrimsShown(getHeight() + mCurrentOffset < getScrimVisibleHeightTrigger());\n        }\n    }\n\n\n    final int getMaxOffsetForPinChild(View child) {\n        final QMUIViewOffsetHelper offsetHelper = getViewOffsetHelper(child);\n        final QMUICollapsingTopBarLayout.LayoutParams lp = (QMUICollapsingTopBarLayout.LayoutParams) child.getLayoutParams();\n        return getHeight() - offsetHelper.getLayoutTop() - child.getHeight() - lp.bottomMargin;\n    }\n\n    public void addOnOffsetUpdateListener(@NonNull OnOffsetUpdateListener listener) {\n        mOnOffsetUpdateListeners.add(listener);\n    }\n\n    public void removeOnOffsetUpdateListener(@NonNull OnOffsetUpdateListener listener) {\n        mOnOffsetUpdateListeners.remove(listener);\n    }\n\n    private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener {\n        OffsetUpdateListener() {\n        }\n\n        @Override\n        public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {\n            mCurrentOffset = verticalOffset;\n\n            final int insetTop = getWindowInsetTop();\n\n            for (int i = 0, z = getChildCount(); i < z; i++) {\n                final View child = getChildAt(i);\n                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                final QMUIViewOffsetHelper offsetHelper = getViewOffsetHelper(child);\n\n                switch (lp.mCollapseMode) {\n                    case QMUICollapsingTopBarLayout.LayoutParams.COLLAPSE_MODE_PIN:\n                        offsetHelper.setTopAndBottomOffset(\n                                QMUILangHelper.constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child)));\n                        break;\n                    case QMUICollapsingTopBarLayout.LayoutParams.COLLAPSE_MODE_PARALLAX:\n                        offsetHelper.setTopAndBottomOffset(\n                                Math.round(-verticalOffset * lp.mParallaxMult));\n                        break;\n                }\n            }\n\n            // Show or hide the scrims if needed\n            updateScrimVisibility();\n\n            if (mStatusBarScrim != null && insetTop > 0) {\n                ViewCompat.postInvalidateOnAnimation(QMUICollapsingTopBarLayout.this);\n            }\n\n            // Update the collapsing text's fraction\n            final int expandRange = getHeight() - ViewCompat.getMinimumHeight(\n                    QMUICollapsingTopBarLayout.this) - insetTop;\n            float expansionFraction = Math.abs(verticalOffset) / (float) expandRange;\n            mCollapsingTextHelper.setExpansionFraction(expansionFraction);\n            for (OnOffsetUpdateListener listener : mOnOffsetUpdateListeners) {\n                listener.onOffsetChanged(\n                        QMUICollapsingTopBarLayout.this, verticalOffset, expansionFraction);\n            }\n        }\n    }\n\n    @Override\n    public boolean intercept(int skinIndex, @NotNull Resources.Theme theme) {\n        if (mContentScrimSkinAttr != 0) {\n            setContentScrimInner(QMUIResHelper.getAttrDrawable(getContext(), theme, mContentScrimSkinAttr));\n        }\n        if (mStatusBarScrimSkinAttr != 0) {\n            setStatusBarScrimInner(QMUIResHelper.getAttrDrawable(getContext(), theme, mStatusBarScrimSkinAttr));\n        }\n\n        if (mCollapsedTextColorSkinAttr != 0) {\n            mCollapsingTextHelper.setCollapsedTextColor(\n                    QMUISkinHelper.getSkinColorStateList(this, mCollapsedTextColorSkinAttr));\n        }\n        if (mExpandedTextColorSkinAttr != 0) {\n            mCollapsingTextHelper.setExpandedTextColor(\n                    QMUISkinHelper.getSkinColorStateList(this, mExpandedTextColorSkinAttr)\n            );\n        }\n        return false;\n    }\n\n    public interface OnOffsetUpdateListener {\n        void onOffsetChanged(QMUICollapsingTopBarLayout layout, int offset, float expandFraction);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIEmptyView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.widget.Button;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.annotation.QMUISkinChangeNotAdapted;\n\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\n/**\n * 用于显示界面的 loading、错误信息提示等状态。\n * <p>\n * 提供了一个 LoadingView、一行标题、一行说明文字、一个按钮, 可以使用 {@link #show(boolean, String, String, String, OnClickListener)} 系列方法控制这些控件的显示内容\n * </p>\n */\npublic class QMUIEmptyView extends ConstraintLayout {\n    private QMUILoadingView mLoadingView;\n    private TextView mTitleTextView;\n    private TextView mDetailTextView;\n    protected Button mButton;\n\n    public QMUIEmptyView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIEmptyView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIEmptyView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.QMUIEmptyView);\n        boolean attrShowLoading = arr.getBoolean(R.styleable.QMUIEmptyView_qmui_show_loading, false);\n        String attrTitleText = arr.getString(R.styleable.QMUIEmptyView_qmui_title_text);\n        String attrDetailText = arr.getString(R.styleable.QMUIEmptyView_qmui_detail_text);\n        String attrBtnText = arr.getString(R.styleable.QMUIEmptyView_qmui_btn_text);\n        arr.recycle();\n        show(attrShowLoading, attrTitleText, attrDetailText, attrBtnText, null);\n    }\n\n    private void init() {\n        LayoutInflater.from(getContext()).inflate(R.layout.qmui_empty_view, this, true);\n        mLoadingView = findViewById(R.id.empty_view_loading);\n        mTitleTextView = findViewById(R.id.empty_view_title);\n        mDetailTextView = findViewById(R.id.empty_view_detail);\n        mButton = findViewById(R.id.empty_view_button);\n    }\n\n    /**\n     * 显示emptyView\n     *\n     * @param loading               是否要显示loading\n     * @param titleText             标题的文字，不需要则传null\n     * @param detailText            详情文字，不需要则传null\n     * @param buttonText            按钮的文字，不需要按钮则传null\n     * @param onButtonClickListener 按钮的onClick监听，不需要则传null\n     */\n    public void show(boolean loading, String titleText, String detailText, String buttonText, OnClickListener onButtonClickListener) {\n        setLoadingShowing(loading);\n        setTitleText(titleText);\n        setDetailText(detailText);\n        setButton(buttonText, onButtonClickListener);\n        show();\n    }\n\n    /**\n     * 用于显示emptyView并且只显示loading的情况，此时title、detail、button都被隐藏\n     *\n     * @param loading 是否显示loading\n     */\n    public void show(boolean loading) {\n        setLoadingShowing(loading);\n        setTitleText(null);\n        setDetailText(null);\n        setButton(null, null);\n        show();\n    }\n\n    /**\n     * 用于显示纯文本的简单调用方法，此时loading、button均被隐藏\n     *\n     * @param titleText  标题的文字，不需要则传null\n     * @param detailText 详情文字，不需要则传null\n     */\n    public void show(String titleText, String detailText) {\n        setLoadingShowing(false);\n        setTitleText(titleText);\n        setDetailText(detailText);\n        setButton(null, null);\n        show();\n    }\n\n    /**\n     * 显示emptyView，不建议直接使用，建议调用带参数的show()方法，方便控制所有子View的显示/隐藏\n     */\n    public void show() {\n        setVisibility(VISIBLE);\n    }\n\n    /**\n     * 隐藏emptyView\n     */\n    public void hide() {\n        setVisibility(GONE);\n        setLoadingShowing(false);\n        setTitleText(null);\n        setDetailText(null);\n        setButton(null, null);\n    }\n\n    public boolean isShowing() {\n        return getVisibility() == VISIBLE;\n    }\n\n    public boolean isLoading() {\n        return mLoadingView.getVisibility() == VISIBLE;\n    }\n\n    public void setLoadingShowing(boolean show) {\n        mLoadingView.setVisibility(show ? VISIBLE : GONE);\n    }\n\n    public void setTitleText(String text) {\n        mTitleTextView.setText(text);\n        mTitleTextView.setVisibility(text != null ? VISIBLE : GONE);\n    }\n\n    public void setDetailText(String text) {\n        mDetailTextView.setText(text);\n        mDetailTextView.setVisibility(text != null ? VISIBLE : GONE);\n    }\n\n    @QMUISkinChangeNotAdapted\n    public void setTitleColor(int color) {\n        mTitleTextView.setTextColor(color);\n    }\n\n    @QMUISkinChangeNotAdapted\n    public void setDetailColor(int color) {\n        mDetailTextView.setTextColor(color);\n    }\n\n    public void setTitleSkinValue(QMUISkinValueBuilder builder) {\n        QMUISkinHelper.setSkinValue(mTitleTextView, builder);\n    }\n\n    public void setDetailSkinValue(QMUISkinValueBuilder builder) {\n        QMUISkinHelper.setSkinValue(mDetailTextView, builder);\n    }\n\n    public void setLoadingSkinValue(QMUISkinValueBuilder builder) {\n        QMUISkinHelper.setSkinValue(mLoadingView, builder);\n    }\n\n    public void setBtnSkinValue(QMUISkinValueBuilder builder) {\n        QMUISkinHelper.setSkinValue(mButton, builder);\n    }\n\n    public void setButton(String text, OnClickListener onClickListener) {\n        mButton.setText(text);\n        mButton.setVisibility(text != null ? VISIBLE : GONE);\n        mButton.setOnClickListener(onClickListener);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIFloatLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.R;\n\n/**\n * 该 layout 使子 View 类似 CSS 中的 float:left 效果, 从左到右排列子 View 并自动换行。支持以下特性：\n * <ul>\n * <li>使用 {@link #setChildVerticalSpacing(int)} 和 {@link #setChildHorizontalSpacing(int)} 控制子 View 的垂直/水平间距</li>\n * <li>使用 {@link #setGravity(int)} 控制子 View 的对齐方向 (左对齐/居中/右对齐)</li>\n * <li>使用 {@link #setMaxNumber(int)} 和 {@link #setMaxLines(int)} 控制子 View 的最多个数或最大行数</li>\n * </ul>\n * <p>在 xml 中采用 {@link com.qmuiteam.qmui.R.styleable#QMUIFloatLayout} 控制以上属性。</p>\n */\npublic class QMUIFloatLayout extends ViewGroup {\n    private int mChildHorizontalSpacing;\n    private int mChildVerticalSpacing;\n    /**\n     * 对齐方式，目前支持 {@link Gravity#CENTER_HORIZONTAL}, {@link Gravity#LEFT} 和 {@link Gravity#RIGHT}\n     */\n    private int mGravity;\n\n    private static final int LINES = 0;\n    private static final int NUMBER = 1;\n    private int mMaxMode = LINES;\n    private int mMaximum = Integer.MAX_VALUE;\n    private int mLineCount = 0;\n    private OnLineCountChangeListener mOnLineCountChangeListener;\n\n    /**\n     * <p>每一行的item数目，下标表示行下标，在onMeasured的时候计算得出，供onLayout去使用。</p>\n     * <p>若mItemNumberInEachLine[x]==0，则表示第x行已经没有item了</p>\n     */\n    private int[] mItemNumberInEachLine;\n    /**\n     * <p>每一行的item的宽度和（包括item直接的间距），下标表示行下标，\n     * 如 mWidthSumInEachLine[x]表示第x行的item的宽度和（包括item直接的间距）</p>\n     * <p>在onMeasured的时候计算得出，供onLayout去使用</p>\n     */\n    private int[] mWidthSumInEachLine;\n    /**\n     * onMeasure过程中实际参与measure的子View个数\n     */\n    private int measuredChildCount;\n\n    public QMUIFloatLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIFloatLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIFloatLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs);\n    }\n\n    private void init(Context context, AttributeSet attrs) {\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUIFloatLayout);\n        mChildHorizontalSpacing = array.getDimensionPixelSize(\n                R.styleable.QMUIFloatLayout_qmui_childHorizontalSpacing, 0);\n        mChildVerticalSpacing = array.getDimensionPixelSize(\n                R.styleable.QMUIFloatLayout_qmui_childVerticalSpacing, 0);\n        mGravity = array.getInteger(R.styleable.QMUIFloatLayout_android_gravity, Gravity.LEFT);\n        int maxLines = array.getInt(R.styleable.QMUIFloatLayout_android_maxLines, -1);\n        if (maxLines >= 0) {\n            setMaxLines(maxLines);\n        }\n        int maxNumber = array.getInt(R.styleable.QMUIFloatLayout_qmui_maxNumber, -1);\n        if (maxNumber >= 0) {\n            setMaxNumber(maxNumber);\n        }\n        array.recycle();\n    }\n\n    @SuppressLint(\"DrawAllocation\")\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);\n        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);\n        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);\n\n        int maxLineHeight = 0;\n\n        int resultWidth;\n        int resultHeight;\n\n        final int count = getChildCount();\n\n        mItemNumberInEachLine = new int[count];\n        mWidthSumInEachLine = new int[count];\n        int lineIndex = 0;\n\n        // 若FloatLayout指定了MATCH_PARENT或固定宽度，则需要使子View换行\n        if (widthSpecMode == MeasureSpec.EXACTLY) {\n            resultWidth = widthSpecSize;\n\n            measuredChildCount = 0;\n\n            // 下一个子View的position\n            int childPositionX = getPaddingLeft();\n            int childPositionY = getPaddingTop();\n\n            // 子View的Right最大可达到的x坐标\n            int childMaxRight = widthSpecSize - getPaddingRight();\n\n            for (int i = 0; i < count; i++) {\n                if (mMaxMode == NUMBER && measuredChildCount >= mMaximum) {\n                    // 超出最多数量，则不再继续\n                    break;\n                } else if (mMaxMode == LINES && lineIndex >= mMaximum) {\n                    // 超出最多行数，则不再继续\n                    break;\n                }\n\n                final View child = getChildAt(i);\n                if (child.getVisibility() == GONE) {\n                    continue;\n                }\n\n                final LayoutParams childLayoutParams = child.getLayoutParams();\n                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,\n                        getPaddingLeft() + getPaddingRight(), childLayoutParams.width);\n                final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,\n                        getPaddingTop() + getPaddingBottom(), childLayoutParams.height);\n                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n\n                final int childw = child.getMeasuredWidth();\n                maxLineHeight = Math.max(maxLineHeight, child.getMeasuredHeight());\n                // 需要换行\n                if (childPositionX + childw > childMaxRight) {\n                    // 如果换行后超出最大行数，则不再继续\n                    if (mMaxMode == LINES) {\n                        if (lineIndex + 1 >= mMaximum) {\n                            break;\n                        }\n                    }\n                    mWidthSumInEachLine[lineIndex] -= mChildHorizontalSpacing; // 后面每次加item都会加上一个space，这样的话每行都会为最后一个item多加一次space，所以在这里减一次\n                    lineIndex++; // 换行\n                    childPositionX = getPaddingLeft(); // 下一行第一个item的x\n                    childPositionY += maxLineHeight + mChildVerticalSpacing; // 下一行第一个item的y\n                }\n                mItemNumberInEachLine[lineIndex]++;\n                mWidthSumInEachLine[lineIndex] += (childw + mChildHorizontalSpacing);\n                childPositionX += (childw + mChildHorizontalSpacing);\n                measuredChildCount++;\n            }\n            // 如果最后一个item不是刚好在行末（即lineCount最后没有+1，也就是mWidthSumInEachLine[lineCount]非0），则要减去最后一个item的space\n            if (mWidthSumInEachLine.length > 0 && mWidthSumInEachLine[lineIndex] > 0) {\n                mWidthSumInEachLine[lineIndex] -= mChildHorizontalSpacing;\n            }\n            if (heightSpecMode == MeasureSpec.UNSPECIFIED) {\n                resultHeight = childPositionY + maxLineHeight + getPaddingBottom();\n            } else if (heightSpecMode == MeasureSpec.AT_MOST) {\n                resultHeight = childPositionY + maxLineHeight + getPaddingBottom();\n                resultHeight = Math.min(resultHeight, heightSpecSize);\n            } else {\n                resultHeight = heightSpecSize;\n            }\n\n        } else {\n            // 不计算换行，直接一行铺开\n            resultWidth = getPaddingLeft() + getPaddingRight();\n            measuredChildCount = 0;\n\n            for (int i = 0; i < count; i++) {\n                if (mMaxMode == NUMBER) {\n                    // 超出最多数量，则不再继续\n                    if (measuredChildCount > mMaximum) {\n                        break;\n                    }\n                } else if (mMaxMode == LINES) {\n                    // 超出最大行数，则不再继续\n                    if (1 > mMaximum) {\n                        break;\n                    }\n                }\n                final View child = getChildAt(i);\n                if (child.getVisibility() == GONE) {\n                    continue;\n                }\n                final LayoutParams childLayoutParams = child.getLayoutParams();\n                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,\n                        getPaddingLeft() + getPaddingRight(), childLayoutParams.width);\n                final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,\n                        getPaddingTop() + getPaddingBottom(), childLayoutParams.height);\n                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n                resultWidth += child.getMeasuredWidth();\n                maxLineHeight = Math.max(maxLineHeight, child.getMeasuredHeight());\n                measuredChildCount++;\n            }\n            if (measuredChildCount > 0) {\n                resultWidth += mChildHorizontalSpacing * (measuredChildCount - 1);\n            }\n            resultHeight = maxLineHeight + getPaddingTop() + getPaddingBottom();\n            if (mItemNumberInEachLine.length > 0) {\n                mItemNumberInEachLine[lineIndex] = count;\n            }\n            if (mWidthSumInEachLine.length > 0) {\n                mWidthSumInEachLine[0] = resultWidth;\n            }\n        }\n        setMeasuredDimension(resultWidth, resultHeight);\n        int meausureLineCount = lineIndex + 1;\n        if(mLineCount != meausureLineCount){\n            if(mOnLineCountChangeListener != null){\n                mOnLineCountChangeListener.onChange(mLineCount, meausureLineCount);\n            }\n            mLineCount = meausureLineCount;\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        final int width = right - left;\n        // 按照不同gravity使用不同的布局，默认是left\n        switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {\n            case Gravity.LEFT:\n                layoutWithGravityLeft(width);\n                break;\n            case Gravity.RIGHT:\n                layoutWithGravityRight(width);\n                break;\n            case Gravity.CENTER_HORIZONTAL:\n                layoutWithGravityCenterHorizontal(width);\n                break;\n            default:\n                layoutWithGravityLeft(width);\n                break;\n        }\n    }\n\n    /**\n     * 将子View居中布局\n     */\n    private void layoutWithGravityCenterHorizontal(int parentWidth) {\n        int nextChildIndex = 0;\n        int nextChildPositionX;\n        int nextChildPositionY = getPaddingTop();\n        int lineHeight = 0;\n        int layoutChildCount = 0;\n        int layoutChildEachLine = 0;\n\n        // 遍历每一行\n        for (int i = 0; i < mItemNumberInEachLine.length; i++) {\n            // 如果这一行已经没item了，则退出循环\n            if (mItemNumberInEachLine[i] == 0) {\n                break;\n            }\n\n            // 遍历该行内的元素，布局每个元素\n            nextChildPositionX = (parentWidth - getPaddingLeft() - getPaddingRight() - mWidthSumInEachLine[i]) / 2 + getPaddingLeft(); // 子 View 的最小 x 值\n            while (layoutChildEachLine < mItemNumberInEachLine[i]) {\n                final View childView = getChildAt(nextChildIndex);\n                if (childView.getVisibility() == GONE) {\n                    nextChildIndex++;\n                    continue;\n                }\n                final int childw = childView.getMeasuredWidth();\n                final int childh = childView.getMeasuredHeight();\n                childView.layout(nextChildPositionX, nextChildPositionY, nextChildPositionX + childw, nextChildPositionY + childh);\n                lineHeight = Math.max(lineHeight, childh);\n                nextChildPositionX += childw + mChildHorizontalSpacing;\n                layoutChildCount++;\n                layoutChildEachLine++;\n                nextChildIndex++;\n                if (layoutChildCount == measuredChildCount) {\n                    break;\n                }\n            }\n\n            if (layoutChildCount == measuredChildCount) {\n                break;\n            }\n\n            // 一行结束了，整理一下，准备下一行\n            nextChildPositionY += (lineHeight + mChildVerticalSpacing);\n            lineHeight = 0;\n            layoutChildEachLine = 0;\n        }\n\n        int childCount = getChildCount();\n        for (int i = nextChildIndex; i < childCount; i++) {\n            final View childView = getChildAt(i);\n            if (childView.getVisibility() == View.GONE) {\n                continue;\n            }\n            childView.layout(0, 0, 0, 0);\n        }\n    }\n\n    /**\n     * 将子View靠左布局\n     */\n    private void layoutWithGravityLeft(int parentWidth) {\n        int childMaxRight = parentWidth - getPaddingRight();\n        int childPositionX = getPaddingLeft();\n        int childPositionY = getPaddingTop();\n        int lineHeight = 0;\n        final int childCount = getChildCount();\n        int layoutChildCount = 0;\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            if (child.getVisibility() == GONE) {\n                continue;\n            }\n            if (layoutChildCount < measuredChildCount) {\n                final int childw = child.getMeasuredWidth();\n                final int childh = child.getMeasuredHeight();\n                if (childPositionX + childw > childMaxRight) {\n                    // 换行\n                    childPositionX = getPaddingLeft();\n                    childPositionY += (lineHeight + mChildVerticalSpacing);\n                    lineHeight = 0;\n                }\n                child.layout(childPositionX, childPositionY, childPositionX + childw, childPositionY + childh);\n                childPositionX += childw + mChildHorizontalSpacing;\n                lineHeight = Math.max(lineHeight, childh);\n                layoutChildCount++;\n            } else {\n                child.layout(0, 0, 0, 0);\n            }\n        }\n    }\n\n    /**\n     * 将子View靠右布局\n     */\n    private void layoutWithGravityRight(int parentWidth) {\n        int nextChildIndex = 0;\n        int nextChildPositionX;\n        int nextChildPositionY = getPaddingTop();\n        int lineHeight = 0;\n        int layoutChildCount = 0;\n        int layoutChildEachLine = 0;\n\n        // 遍历每一行\n        for (int i = 0; i < mItemNumberInEachLine.length; i++) {\n            // 如果这一行已经没item了，则退出循环\n            if (mItemNumberInEachLine[i] == 0) {\n                break;\n            }\n\n            // 遍历该行内的元素，布局每个元素\n            nextChildPositionX = parentWidth - getPaddingRight() - mWidthSumInEachLine[i]; // 初始值为子 View 的最小 x 值\n            while (layoutChildEachLine < mItemNumberInEachLine[i]) {\n                final View childView = getChildAt(nextChildIndex);\n                if (childView.getVisibility() == GONE) {\n                    nextChildIndex++;\n                    continue;\n                }\n                final int childw = childView.getMeasuredWidth();\n                final int childh = childView.getMeasuredHeight();\n                childView.layout(nextChildPositionX, nextChildPositionY, nextChildPositionX + childw, nextChildPositionY + childh);\n                lineHeight = Math.max(lineHeight, childh);\n                nextChildPositionX += childw + mChildHorizontalSpacing;\n                layoutChildCount++;\n                layoutChildEachLine++;\n                nextChildIndex++;\n                if (layoutChildCount == measuredChildCount) {\n                    break;\n                }\n            }\n            if (layoutChildCount == measuredChildCount) {\n                break;\n            }\n\n            // 一行结束了，整理一下，准备下一行\n            nextChildPositionY += (lineHeight + mChildVerticalSpacing);\n            lineHeight = 0;\n            layoutChildEachLine = 0;\n        }\n\n        int childCount = getChildCount();\n        for (int i = nextChildIndex; i < childCount; i++) {\n            final View childView = getChildAt(i);\n            if (childView.getVisibility() == View.GONE) {\n                continue;\n            }\n            childView.layout(0, 0, 0, 0);\n        }\n    }\n\n    /**\n     * 设置子 View 的对齐方式，目前支持 {@link Gravity#CENTER_HORIZONTAL}, {@link Gravity#LEFT} 和 {@link Gravity#RIGHT}\n     */\n    public void setGravity(int gravity) {\n        if (mGravity != gravity) {\n            mGravity = gravity;\n            requestLayout();\n        }\n    }\n\n    public int getGravity() {\n        return mGravity;\n    }\n\n    /**\n     * 设置最多可显示的子View个数\n     * 注意该方法不会改变子View的个数，只会影响显示出来的子View个数\n     *\n     * @param maxNumber 最多可显示的子View个数\n     */\n    public void setMaxNumber(int maxNumber) {\n        mMaximum = maxNumber;\n        mMaxMode = NUMBER;\n        requestLayout();\n    }\n\n    /**\n     * 获取最多可显示的子View个数\n     */\n    public int getMaxNumber() {\n        return mMaxMode == NUMBER ? mMaximum : -1;\n    }\n\n    /**\n     * 设置最多可显示的行数\n     * 注意该方法不会改变子View的个数，只会影响显示出来的子View个数\n     *\n     * @param maxLines 最多可显示的行数\n     */\n    public void setMaxLines(int maxLines) {\n        mMaximum = maxLines;\n        mMaxMode = LINES;\n        requestLayout();\n    }\n\n    public void setOnLineCountChangeListener(OnLineCountChangeListener onLineCountChangeListener) {\n        mOnLineCountChangeListener = onLineCountChangeListener;\n    }\n\n    public int getLineCount() {\n        return mLineCount;\n    }\n\n    /**\n     * 获取最多可显示的行数\n     *\n     * @return 没有限制时返回-1\n     */\n    public int getMaxLines() {\n        return mMaxMode == LINES ? mMaximum : -1;\n    }\n\n    /**\n     * 设置子 View 的水平间距\n     */\n    public void setChildHorizontalSpacing(int spacing) {\n        mChildHorizontalSpacing = spacing;\n        invalidate();\n    }\n\n    /**\n     * 设置子 View 的垂直间距\n     */\n    public void setChildVerticalSpacing(int spacing) {\n        mChildVerticalSpacing = spacing;\n        invalidate();\n    }\n\n    public interface OnLineCountChangeListener {\n        void onChange(int oldLineCount, int newLineCount);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIFontFitTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\n\nimport androidx.appcompat.widget.AppCompatTextView;\n\n/**\n * 使 {@link android.widget.TextView} 在宽度固定的情况下，文字多到一行放不下时能缩小文字大小来自适应\n *\n * http://stackoverflow.com/questions/2617266/how-to-adjust-text-font-size-to-fit-textview\n */\npublic class QMUIFontFitTextView extends AppCompatTextView {\n\n    private Paint mTestPaint;\n    private float minSize;\n    private float maxSize;\n\n    public QMUIFontFitTextView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIFontFitTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        mTestPaint = new Paint();\n        mTestPaint.set(this.getPaint());\n\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUIFontFitTextView);\n        minSize = array.getDimensionPixelSize(\n                R.styleable.QMUIFontFitTextView_qmui_minTextSize, Math.round(14 * QMUIDisplayHelper.DENSITY));\n        maxSize = array.getDimensionPixelSize(\n                R.styleable.QMUIFontFitTextView_qmui_maxTextSize, Math.round(18 * QMUIDisplayHelper.DENSITY));\n        array.recycle();\n        //max size defaults to the initially specified text size unless it is too small\n    }\n\n    /* Re size the font so the specified text fits in the text box\n     * assuming the text box is the specified width.\n     */\n    private void refitText(String text, int textWidth) {\n        if (textWidth <= 0)\n            return;\n        int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();\n        float hi = maxSize;\n        float lo = minSize;\n        float size;\n        final float threshold = 0.5f; // How close we have to be\n\n        mTestPaint.set(this.getPaint());\n\n        mTestPaint.setTextSize(maxSize);\n        if(mTestPaint.measureText(text) <= targetWidth) {\n            lo = maxSize;\n        } else {\n            mTestPaint.setTextSize(minSize);\n            if(mTestPaint.measureText(text) < targetWidth) {\n                while((hi - lo) > threshold) {\n                    size = (hi+lo)/2;\n                    mTestPaint.setTextSize(size);\n                    if(mTestPaint.measureText(text) >= targetWidth)\n                        hi = size; // too big\n                    else\n                        lo = size; // too small\n                }\n            }\n        }\n\n        // Use lo so that we undershoot rather than overshoot\n        this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);\n        int height = getMeasuredHeight();\n        refitText(this.getText().toString(), parentWidth);\n        this.setMeasuredDimension(parentWidth, height);\n    }\n\n    @Override\n    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {\n        refitText(text.toString(), this.getWidth());\n    }\n\n    @Override\n    protected void onSizeChanged (int w, int h, int oldw, int oldh) {\n        if (w != oldw) {\n            refitText(this.getText().toString(), w);\n        }\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIItemViewsAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.core.util.Pools;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.R;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 一个带 cache 功能的“列表型数据-View”的适配器，适用于自定义 {@link View} 需要显示重复单元 {@link android.widget.ListView} 的情景，\n * cache 功能主要是保证在需要多次刷新数据或布局的情况下（{@link android.widget.ListView} 或 {@link RecyclerView} 的 itemView）\n * 复用已存在的 {@link View}。\n * QMUI 用于 {@link com.qmuiteam.qmui.widget.tab.QMUITabSegment} 中 {@link com.qmuiteam.qmui.widget.tab.QMUITab} 与数据的适配。\n *\n * @author cginechen\n * @date 2016-11-27\n */\n\npublic abstract class QMUIItemViewsAdapter<T, V extends View> {\n    private Pools.Pool<V> mCachePool;\n    private List<T> mItemData = new ArrayList<>();\n    // 不能简单的用mParentView的子views，因为可能mParentView有一些装饰子view,不应该归adapter管理\n    private List<V> mViews = new ArrayList<>();\n    private ViewGroup mParentView;\n\n    public QMUIItemViewsAdapter(ViewGroup parentView) {\n        mParentView = parentView;\n    }\n\n    public void detach(int count) {\n        int childCount = mViews.size();\n        while (childCount > 0 && count > 0) {\n            V view = mViews.remove(childCount - 1);\n            if (mCachePool == null) {\n                mCachePool = new Pools.SimplePool<>(12);\n            }\n\n            // 做简单cache，如果V需要动态添加子view，则业务保证不做cache\n            Object notCacheTag = view.getTag(R.id.qmui_view_can_not_cache_tag);\n            if (notCacheTag == null || !(boolean) notCacheTag) {\n                try {\n                    onViewRecycled(view);\n                    mCachePool.release(view);\n                } catch (Exception ignored) {\n                }\n            }\n\n            mParentView.removeView(view);\n            childCount--;\n            count--;\n        }\n    }\n\n    public void clear() {\n        mItemData.clear();\n        detach(mViews.size());\n    }\n\n    private V getView() {\n        V v = mCachePool != null ? mCachePool.acquire() : null;\n        if (v == null) {\n            v = createView(mParentView);\n        }\n        return v;\n    }\n\n    protected abstract V createView(ViewGroup parentView);\n\n    protected void onViewRecycled(V v){\n\n    }\n\n    public QMUIItemViewsAdapter<T, V> addItem(T item) {\n        mItemData.add(item);\n        return this;\n    }\n\n    public void setup() {\n        int itemCount = mItemData.size();\n        int childCount = mViews.size();\n        int i;\n\n        if (childCount > itemCount) {\n            detach(childCount - itemCount);\n        } else if (childCount < itemCount) {\n            for (i = 0; i < itemCount - childCount; i++) {\n                V view = getView();\n                mParentView.addView(view);\n                mViews.add(view);\n            }\n        }\n\n        for (i = 0; i < itemCount; i++) {\n            V view = mViews.get(i);\n            T item = mItemData.get(i);\n            bind(item, view, i);\n        }\n        mParentView.invalidate();\n        mParentView.requestLayout();\n\n    }\n\n    public T getItem(int position) {\n        if (mItemData == null) {\n            return null;\n        }\n        if (position < 0 || position >= mItemData.size()) {\n            return null;\n        }\n        return mItemData.get(position);\n    }\n\n    public void replaceItem(int position, T data) throws IllegalAccessException {\n        if (position < mItemData.size() && position >= 0) {\n            mItemData.set(position, data);\n        } else {\n            throw new IllegalAccessException(\"替换数据不存在\");\n        }\n\n    }\n\n    protected abstract void bind(T item, V view, int position);\n\n    public List<V> getViews() {\n        return mViews;\n    }\n\n    public int getSize() {\n        if (mItemData == null) {\n            return 0;\n        }\n        return mItemData.size();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUILoadingView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\n\nimport androidx.annotation.NonNull;\nimport androidx.collection.SimpleArrayMap;\n\n/**\n * 用于显示 Loading 的 {@link View}，支持颜色和大小的设置。\n *\n * @author cginechen\n * @date 2016-09-21\n */\npublic class QMUILoadingView extends View implements IQMUISkinDefaultAttrProvider {\n\n    private int mSize;\n    private int mPaintColor;\n    private int mAnimateValue = 0;\n    private ValueAnimator mAnimator;\n    private Paint mPaint;\n    private static final int LINE_COUNT = 12;\n    private static final int DEGREE_PER_LINE = 360 / LINE_COUNT;\n    private static SimpleArrayMap<String, Integer> sDefaultAttrs;\n\n    static {\n        sDefaultAttrs = new SimpleArrayMap<>();\n        sDefaultAttrs.put(QMUISkinValueBuilder.TINT_COLOR, R.attr.qmui_skin_support_loading_color);\n    }\n\n    public QMUILoadingView(Context context) {\n        this(context, null);\n    }\n\n    public QMUILoadingView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUILoadingStyle);\n    }\n\n    public QMUILoadingView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.QMUILoadingView, defStyleAttr, 0);\n        mSize = array.getDimensionPixelSize(R.styleable.QMUILoadingView_qmui_loading_view_size, QMUIDisplayHelper.dp2px(context, 32));\n        mPaintColor = array.getInt(R.styleable.QMUILoadingView_android_color, Color.WHITE);\n        array.recycle();\n        initPaint();\n\n    }\n\n    public QMUILoadingView(Context context, int size, int color) {\n        super(context);\n        mSize = size;\n        mPaintColor = color;\n        initPaint();\n    }\n\n    private void initPaint() {\n        mPaint = new Paint();\n        mPaint.setColor(mPaintColor);\n        mPaint.setAntiAlias(true);\n        mPaint.setStrokeCap(Paint.Cap.ROUND);\n    }\n\n    public void setColor(int color) {\n        mPaintColor = color;\n        mPaint.setColor(color);\n        invalidate();\n    }\n\n    public void setSize(int size) {\n        mSize = size;\n        requestLayout();\n    }\n\n    private ValueAnimator.AnimatorUpdateListener mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {\n        @Override\n        public void onAnimationUpdate(ValueAnimator animation) {\n            mAnimateValue = (int) animation.getAnimatedValue();\n            invalidate();\n        }\n    };\n\n    public void start() {\n        if (mAnimator == null) {\n            mAnimator = ValueAnimator.ofInt(0, LINE_COUNT - 1);\n            mAnimator.addUpdateListener(mUpdateListener);\n            mAnimator.setDuration(600);\n            mAnimator.setRepeatMode(ValueAnimator.RESTART);\n            mAnimator.setRepeatCount(ValueAnimator.INFINITE);\n            mAnimator.setInterpolator(new LinearInterpolator());\n            mAnimator.start();\n        } else if (!mAnimator.isStarted()) {\n            mAnimator.start();\n        }\n    }\n\n    public void stop() {\n        if (mAnimator != null) {\n            mAnimator.removeUpdateListener(mUpdateListener);\n            mAnimator.removeAllUpdateListeners();\n            mAnimator.cancel();\n            mAnimator = null;\n        }\n    }\n\n    private void drawLoading(Canvas canvas, int rotateDegrees) {\n        int width = mSize / 12, height = mSize / 6;\n        mPaint.setStrokeWidth(width);\n\n        canvas.rotate(rotateDegrees, mSize / 2, mSize / 2);\n        canvas.translate(mSize / 2, mSize / 2);\n\n        for (int i = 0; i < LINE_COUNT; i++) {\n            canvas.rotate(DEGREE_PER_LINE);\n            mPaint.setAlpha((int) (255f * (i + 1) / LINE_COUNT));\n            canvas.translate(0, -mSize / 2 + width / 2);\n            canvas.drawLine(0, 0, 0, height, mPaint);\n            canvas.translate(0, mSize / 2 - width / 2);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        setMeasuredDimension(mSize, mSize);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);\n        drawLoading(canvas, mAnimateValue * DEGREE_PER_LINE);\n        canvas.restoreToCount(saveCount);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        start();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        stop();\n    }\n\n    @Override\n    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {\n        super.onVisibilityChanged(changedView, visibility);\n        if (visibility == VISIBLE) {\n            start();\n        } else {\n            stop();\n        }\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultAttrs;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUINotchConsumeLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.util.QMUINotchHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\npublic class QMUINotchConsumeLayout extends FrameLayout {\n    public QMUINotchConsumeLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUINotchConsumeLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUINotchConsumeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        QMUIWindowInsetHelper.setOnApplyWindowInsetsListener(this, new androidx.core.view.OnApplyWindowInsetsListener() {\n            @Override\n            public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {\n                notifyInsetMaybeChanged();\n                return insets;\n            }\n        }, true);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (!QMUINotchHelper.isNotchOfficialSupport()) {\n            notifyInsetMaybeChanged();\n        }\n    }\n\n    @Override\n    protected void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        if (!QMUINotchHelper.isNotchOfficialSupport()) {\n            notifyInsetMaybeChanged();\n        }\n    }\n\n    public boolean notifyInsetMaybeChanged() {\n        setPadding(\n                QMUINotchHelper.getSafeInsetLeft(this),\n                QMUINotchHelper.getSafeInsetTop(this),\n                QMUINotchHelper.getSafeInsetRight(this),\n                QMUINotchHelper.getSafeInsetBottom(this)\n        );\n        return true;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIObservableScrollView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.ScrollView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 可以监听滚动事件的 {@link ScrollView}，并能在滚动回调中获取每次滚动前后的偏移量。\n * <p>\n * 由于 {@link ScrollView} 没有类似于 addOnScrollChangedListener 的方法可以监听滚动事件，所以需要通过重写 {@link android.view.View#onScrollChanged}，来触发滚动监听\n *\n * @author chantchen\n * @date 2015-08-25\n */\npublic class QMUIObservableScrollView extends ScrollView {\n\n    private int mScrollOffset = 0;\n\n    private List<OnScrollChangedListener> mOnScrollChangedListeners;\n\n    public QMUIObservableScrollView(Context context) {\n        super(context);\n    }\n\n    public QMUIObservableScrollView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public void addOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) {\n        if (mOnScrollChangedListeners == null) {\n            mOnScrollChangedListeners = new ArrayList<>();\n        }\n        if (mOnScrollChangedListeners.contains(onScrollChangedListener)) {\n            return;\n        }\n        mOnScrollChangedListeners.add(onScrollChangedListener);\n    }\n\n    public void removeOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) {\n        if (mOnScrollChangedListeners == null) {\n            return;\n        }\n        mOnScrollChangedListeners.remove(onScrollChangedListener);\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        mScrollOffset = t;\n        if (mOnScrollChangedListeners != null && !mOnScrollChangedListeners.isEmpty()) {\n            for (OnScrollChangedListener listener : mOnScrollChangedListeners) {\n                listener.onScrollChanged(this, l, t, oldl, oldt);\n            }\n        }\n    }\n\n    public int getScrollOffset() {\n        return mScrollOffset;\n    }\n\n    public interface OnScrollChangedListener {\n        void onScrollChanged(QMUIObservableScrollView scrollView, int l, int t, int oldl, int oldt);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIPagerAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.util.SparseArray;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.viewpager.widget.PagerAdapter;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\npublic abstract class QMUIPagerAdapter extends PagerAdapter {\n    private SparseArray<Object> mScrapItems = new SparseArray<>();\n\n    public QMUIPagerAdapter() {\n    }\n\n\n    /**\n     * Hydrating an object is taking an object that exists in memory,\n     * that doesn't yet contain any domain data (\"real\" data),\n     * and then populating it with domain data.\n     */\n    @NonNull\n    protected abstract Object hydrate(@NonNull ViewGroup container, int position);\n\n    protected abstract void populate(@NonNull ViewGroup container, @NonNull Object item, int position);\n\n    protected abstract void destroy(@NonNull ViewGroup container, int position, @NonNull Object object);\n\n    @Override\n    @NonNull\n    public Object instantiateItem(@NonNull ViewGroup container, int position) {\n        Object item = mScrapItems.get(position);\n        if (item == null) {\n            item = hydrate(container, position);\n            mScrapItems.put(position, item);\n        }\n        populate(container, item, position);\n        return item;\n    }\n\n    @Override\n    public final void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {\n        destroy(container, position, object);\n    }\n\n    /**\n     * sometimes you may need to perform some operations on all items,\n     * such as perform cleanup when the ViewPager is destroyed\n     * once the action return true, then do not handle remain items\n     *\n     * @param action\n     */\n    public void each(@NonNull Action action) {\n        int size = mScrapItems.size();\n        for (int i = 0; i < size; i++) {\n            Object item = mScrapItems.valueAt(i);\n            if (action.call(item)) {\n                break;\n            }\n        }\n    }\n\n    public interface Action {\n        /**\n         * @return true to intercept forEach\n         */\n        boolean call(Object item);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIProgressBar.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.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.Point;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\n\n/**\n * 一个进度条控件，通过颜色变化显示进度，支持环形和矩形两种形式，主要特性如下：\n * <ol>\n * <li>支持在进度条中以文字形式显示进度，支持修改文字的颜色和大小。</li>\n * <li>可以通过 xml 属性修改进度背景色，当前进度颜色，进度条尺寸。</li>\n * <li>支持限制进度的最大值。</li>\n * </ol>\n *\n * @author cginechen\n * @date 2015-07-29\n */\npublic class QMUIProgressBar extends View {\n\n    public final static int TYPE_RECT = 0;\n    public final static int TYPE_ROUND_RECT = 1;\n    public final static int TYPE_CIRCLE = 2;\n    public final static int TYPE_FILL_CIRCLE = 3;\n\n    public final static int TOTAL_DURATION = 1000;\n    public final static int DEFAULT_PROGRESS_COLOR = Color.BLUE;\n    public final static int DEFAULT_BACKGROUND_COLOR = Color.GRAY;\n    public final static int DEFAULT_TEXT_SIZE = 20;\n    public final static int DEFAULT_TEXT_COLOR = Color.BLACK;\n    private final static int PENDING_VALUE_NOT_SET = -1;\n    /*circle_progress member*/\n    public static int DEFAULT_STROKE_WIDTH = QMUIDisplayHelper.dpToPx(40);\n    QMUIProgressBarTextGenerator mQMUIProgressBarTextGenerator;\n    /*rect_progress member*/\n    RectF mBgRect;\n    RectF mProgressRect;\n    /*common member*/\n    private int mWidth;\n    private int mHeight;\n    private int mType;\n    private int mProgressColor;\n    private int mBackgroundColor;\n    private int mMaxValue;\n    private int mValue;\n    private int mPendingValue;\n    private long mAnimationStartTime;\n    private int mAnimationDistance;\n    private int mAnimationDuration;\n    private int mTextSize;\n    private int mTextColor;\n    private boolean mRoundCap;\n    private Paint mBackgroundPaint = new Paint();\n    private Paint mPaint = new Paint();\n    private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private RectF mArcOval = new RectF();\n    private String mText = \"\";\n    private int mStrokeWidth;\n    private float mCircleRadius;\n    private Point mCenterPoint;\n    private OnProgressChangeListener mOnProgressChangeListener;\n    private Runnable mNotifyProgressChangeAction = new Runnable() {\n        @Override\n        public void run() {\n            if(mOnProgressChangeListener != null){\n                mOnProgressChangeListener.onProgressChange(QMUIProgressBar.this, mValue, mMaxValue);\n            }\n        }\n    };\n\n\n    public QMUIProgressBar(Context context) {\n        super(context);\n        setup(context, null);\n    }\n\n    public QMUIProgressBar(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setup(context, attrs);\n    }\n\n    public QMUIProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setup(context, attrs);\n    }\n\n    public void setup(Context context, AttributeSet attrs) {\n        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QMUIProgressBar);\n        mType = array.getInt(R.styleable.QMUIProgressBar_qmui_type, TYPE_RECT);\n        mProgressColor = array.getColor(R.styleable.QMUIProgressBar_qmui_progress_color, DEFAULT_PROGRESS_COLOR);\n        mBackgroundColor = array.getColor(R.styleable.QMUIProgressBar_qmui_background_color, DEFAULT_BACKGROUND_COLOR);\n\n        mMaxValue = array.getInt(R.styleable.QMUIProgressBar_qmui_max_value, 100);\n        mValue = array.getInt(R.styleable.QMUIProgressBar_qmui_value, 0);\n\n        mRoundCap = array.getBoolean(R.styleable.QMUIProgressBar_qmui_stroke_round_cap, false);\n\n        mTextSize = DEFAULT_TEXT_SIZE;\n        if (array.hasValue(R.styleable.QMUIProgressBar_android_textSize)) {\n            mTextSize = array.getDimensionPixelSize(R.styleable.QMUIProgressBar_android_textSize, DEFAULT_TEXT_SIZE);\n        }\n        mTextColor = DEFAULT_TEXT_COLOR;\n        if (array.hasValue(R.styleable.QMUIProgressBar_android_textColor)) {\n            mTextColor = array.getColor(R.styleable.QMUIProgressBar_android_textColor, DEFAULT_TEXT_COLOR);\n        }\n\n        if (mType == TYPE_CIRCLE || mType == TYPE_FILL_CIRCLE) {\n            mStrokeWidth = array.getDimensionPixelSize(R.styleable.QMUIProgressBar_qmui_stroke_width, DEFAULT_STROKE_WIDTH);\n        }\n        array.recycle();\n        configPaint(mTextColor, mTextSize, mRoundCap, mStrokeWidth);\n\n        setProgress(mValue);\n    }\n\n    public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {\n        mOnProgressChangeListener = onProgressChangeListener;\n    }\n\n    public void setStrokeWidth(int strokeWidth) {\n        if(mStrokeWidth != strokeWidth){\n            mStrokeWidth = strokeWidth;\n            if(mWidth > 0){\n                configShape();\n            }\n            configPaint(mTextColor, mTextSize, mRoundCap, mStrokeWidth);\n            invalidate();\n        }\n    }\n\n    private void configShape() {\n        if (mType == TYPE_RECT || mType == TYPE_ROUND_RECT) {\n            mBgRect = new RectF(getPaddingLeft(), getPaddingTop(), mWidth + getPaddingLeft(), mHeight + getPaddingTop());\n            mProgressRect = new RectF();\n        } else {\n            mCircleRadius = (Math.min(mWidth, mHeight) - mStrokeWidth) / 2f - 0.5f;\n            mCenterPoint = new Point(mWidth / 2, mHeight / 2);\n        }\n    }\n\n    private void configPaint(int textColor, int textSize, boolean isRoundCap, int strokeWidth) {\n        mPaint.setColor(mProgressColor);\n        mBackgroundPaint.setColor(mBackgroundColor);\n        if (mType == TYPE_RECT || mType == TYPE_ROUND_RECT) {\n            mPaint.setStyle(Paint.Style.FILL);\n            mPaint.setStrokeCap(Paint.Cap.BUTT);\n            mBackgroundPaint.setStyle(Paint.Style.FILL);\n        } else if(mType == TYPE_FILL_CIRCLE){\n            mPaint.setStyle(Paint.Style.FILL);\n            mPaint.setAntiAlias(true);\n            mPaint.setStrokeCap(Paint.Cap.BUTT);\n            mBackgroundPaint.setStyle(Paint.Style.STROKE);\n            mBackgroundPaint.setStrokeWidth(strokeWidth);\n            mBackgroundPaint.setAntiAlias(true);\n        } else {\n            mPaint.setStyle(Paint.Style.STROKE);\n            mPaint.setStrokeWidth(strokeWidth);\n            mPaint.setAntiAlias(true);\n            if (isRoundCap) {\n                mPaint.setStrokeCap(Paint.Cap.ROUND);\n            }else{\n                mPaint.setStrokeCap(Paint.Cap.BUTT);\n            }\n            mBackgroundPaint.setStyle(Paint.Style.STROKE);\n            mBackgroundPaint.setStrokeWidth(strokeWidth);\n            mBackgroundPaint.setAntiAlias(true);\n        }\n        mTextPaint.setColor(textColor);\n        mTextPaint.setTextSize(textSize);\n        mTextPaint.setTextAlign(Paint.Align.CENTER);\n    }\n\n    public void setType(int type) {\n        mType = type;\n        configPaint(mTextColor, mTextSize, mRoundCap, mStrokeWidth);\n        invalidate();\n    }\n\n    public void setBarColor(int backgroundColor, int progressColor) {\n        mBackgroundColor = backgroundColor;\n        mProgressColor = progressColor;\n        mBackgroundPaint.setColor(mBackgroundColor);\n        mPaint.setColor(mProgressColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBackgroundColor(int backgroundColor) {\n        mBackgroundColor = backgroundColor;\n        mBackgroundPaint.setColor(mBackgroundColor);\n        invalidate();\n    }\n\n    public void setProgressColor(int progressColor) {\n        mProgressColor = progressColor;\n        mPaint.setColor(mProgressColor);\n        invalidate();\n    }\n\n    /**\n     * 设置进度文案的文字大小\n     *\n     * @see #setTextColor(int)\n     * @see #setQMUIProgressBarTextGenerator(QMUIProgressBarTextGenerator)\n     */\n    public void setTextSize(int textSize) {\n        mTextPaint.setTextSize(textSize);\n        invalidate();\n    }\n\n    /**\n     * 设置进度文案的文字颜色\n     *\n     * @see #setTextSize(int)\n     * @see #setQMUIProgressBarTextGenerator(QMUIProgressBarTextGenerator)\n     */\n    public void setTextColor(int textColor) {\n        mTextPaint.setColor(textColor);\n        invalidate();\n    }\n\n    /**\n     * 设置环形进度条的两端是否有圆形的线帽，类型为{@link #TYPE_CIRCLE}时生效\n     */\n    public void setStrokeRoundCap(boolean isRoundCap) {\n        mPaint.setStrokeCap(isRoundCap ? Paint.Cap.ROUND : Paint.Cap.BUTT);\n        invalidate();\n    }\n\n    /**\n     * 通过 {@link QMUIProgressBarTextGenerator} 设置进度文案\n     */\n    public void setQMUIProgressBarTextGenerator(QMUIProgressBarTextGenerator QMUIProgressBarTextGenerator) {\n        mQMUIProgressBarTextGenerator = QMUIProgressBarTextGenerator;\n    }\n\n    public QMUIProgressBarTextGenerator getQMUIProgressBarTextGenerator() {\n        return mQMUIProgressBarTextGenerator;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        if (mPendingValue != PENDING_VALUE_NOT_SET) {\n            long elapsed = System.currentTimeMillis() - mAnimationStartTime;\n            if (elapsed >= mAnimationDuration) {\n                mValue = mPendingValue;\n                post(mNotifyProgressChangeAction);\n                mPendingValue = PENDING_VALUE_NOT_SET;\n            } else {\n                mValue = (int) (mPendingValue - (1f - ((float) elapsed / mAnimationDuration)) * mAnimationDistance);\n                post(mNotifyProgressChangeAction);\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n        }\n\n        if (mQMUIProgressBarTextGenerator != null) {\n            mText = mQMUIProgressBarTextGenerator.generateText(this, mValue, mMaxValue);\n        }\n        if(((mType == TYPE_RECT || mType == TYPE_ROUND_RECT) && mBgRect == null) ||\n                ((mType == TYPE_CIRCLE || mType == TYPE_FILL_CIRCLE) && mCenterPoint == null)){\n            // npe protect, sometimes measure may not be called by parent.\n            configShape();\n        }\n        if (mType == TYPE_RECT) {\n            drawRect(canvas);\n        } else if (mType == TYPE_ROUND_RECT) {\n            drawRoundRect(canvas);\n        } else {\n            drawCircle(canvas, mType == TYPE_FILL_CIRCLE);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        mWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\n        mHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\n\n        configShape();\n        setMeasuredDimension(mWidth, mHeight);\n    }\n\n    private void drawRect(Canvas canvas) {\n        canvas.drawRect(mBgRect, mBackgroundPaint);\n        mProgressRect.set(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + parseValueToWidth(), getPaddingTop() + mHeight);\n        canvas.drawRect(mProgressRect, mPaint);\n        if (mText != null && mText.length() > 0) {\n            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();\n            float baseline = mBgRect.top + (mBgRect.height() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;\n            canvas.drawText(mText, mBgRect.centerX(), baseline, mTextPaint);\n        }\n    }\n\n    private void drawRoundRect(Canvas canvas) {\n        float round = mHeight / 2f;\n        canvas.drawRoundRect(mBgRect, round, round, mBackgroundPaint);\n        mProgressRect.set(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + parseValueToWidth(), getPaddingTop() + mHeight);\n        canvas.drawRoundRect(mProgressRect, round, round, mPaint);\n        if (mText != null && mText.length() > 0) {\n            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();\n            float baseline = mBgRect.top + (mBgRect.height() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;\n            canvas.drawText(mText, mBgRect.centerX(), baseline, mTextPaint);\n        }\n    }\n\n    private void drawCircle(Canvas canvas, boolean useCenter) {\n        canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mCircleRadius, mBackgroundPaint);\n        mArcOval.left = mCenterPoint.x - mCircleRadius;\n        mArcOval.right = mCenterPoint.x + mCircleRadius;\n        mArcOval.top = mCenterPoint.y - mCircleRadius;\n        mArcOval.bottom = mCenterPoint.y + mCircleRadius;\n        if (mValue > 0) {\n            canvas.drawArc(mArcOval, 270, 360f * mValue / mMaxValue, useCenter, mPaint);\n        }\n        if (mText != null && mText.length() > 0) {\n            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();\n            float baseline = mArcOval.top + (mArcOval.height() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;\n            canvas.drawText(mText, mCenterPoint.x, baseline, mTextPaint);\n        }\n    }\n\n    private int parseValueToWidth() {\n        return mWidth * mValue / mMaxValue;\n    }\n\n    public int getProgress() {\n        return mValue;\n    }\n\n    public void setProgress(int progress) {\n        setProgress(progress, true);\n    }\n\n    public void setProgress(int progress, boolean animated) {\n        if (progress > mMaxValue || progress < 0) {\n            return;\n        }\n\n        if ((mPendingValue == PENDING_VALUE_NOT_SET && mValue == progress) ||\n                (mPendingValue != PENDING_VALUE_NOT_SET && mPendingValue == progress)) {\n            return;\n        }\n\n        if (!animated) {\n            mPendingValue = PENDING_VALUE_NOT_SET;\n            mValue = progress;\n            mNotifyProgressChangeAction.run();\n            invalidate();\n        } else {\n            mAnimationDuration = Math.abs((int) (TOTAL_DURATION * (mValue - progress) / (float) mMaxValue));\n            mAnimationStartTime = System.currentTimeMillis();\n            mAnimationDistance = progress - mValue;\n            mPendingValue = progress;\n            invalidate();\n        }\n    }\n\n    public int getMaxValue() {\n        return mMaxValue;\n    }\n\n    public void setMaxValue(int maxValue) {\n        mMaxValue = maxValue;\n    }\n\n    public interface QMUIProgressBarTextGenerator {\n        /**\n         * 设置进度文案, {@link QMUIProgressBar} 会在进度更新时调用该方法获取要显示的文案\n         *\n         * @param value    当前进度值\n         * @param maxValue 最大进度值\n         * @return 进度文案\n         */\n        String generateText(QMUIProgressBar progressBar, int value, int maxValue);\n    }\n\n    public interface OnProgressChangeListener {\n        void onProgressChange(QMUIProgressBar progressBar, int currentValue, int maxValue);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIRadiusImageView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapShader;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffColorFilter;\nimport android.graphics.RectF;\nimport android.graphics.Shader;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\nimport com.qmuiteam.qmui.R;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.AppCompatImageView;\n\n/**\n * 提供为图片添加圆角、边框、剪裁到圆形或其他形状等功能。\n * shown radius image in view, is different to {@link QMUIRadiusImageView2}\n *\n * @author cginechen\n * @date 2015-07-09\n */\npublic class QMUIRadiusImageView extends AppCompatImageView {\n    private static final int DEFAULT_BORDER_COLOR = Color.GRAY;\n\n    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;\n    private static final int COLOR_DRAWABLE_DIMEN = 2;\n\n    private boolean mIsSelected = false;\n    private boolean mIsOval = false;\n    private boolean mIsCircle = false;\n\n    private int mBorderWidth;\n    private int mBorderColor;\n\n    private int mSelectedBorderWidth;\n    private int mSelectedBorderColor;\n    private int mSelectedMaskColor;\n    private boolean mIsTouchSelectModeEnabled = true;\n\n    private int mCornerRadius;\n\n    private Paint mBitmapPaint;\n    private Paint mBorderPaint;\n    private ColorFilter mColorFilter;\n    private ColorFilter mSelectedColorFilter;\n    private BitmapShader mBitmapShader;\n    private boolean mNeedResetShader = false;\n\n    private RectF mRectF = new RectF();\n    private RectF mDrawRectF = new RectF();\n\n    private Bitmap mBitmap;\n\n    private Matrix mMatrix;\n    private int mWidth;\n    private int mHeight;\n    private ScaleType mLastCalculateScaleType;\n\n    public QMUIRadiusImageView(Context context) {\n        this(context, null, R.attr.QMUIRadiusImageViewStyle);\n    }\n\n    public QMUIRadiusImageView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIRadiusImageViewStyle);\n    }\n\n    public QMUIRadiusImageView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        mBorderPaint = new Paint();\n        mBorderPaint.setAntiAlias(true);\n        mBorderPaint.setStyle(Paint.Style.STROKE);\n        mMatrix = new Matrix();\n\n        setScaleType(ScaleType.CENTER_CROP);\n\n        TypedArray array = context.obtainStyledAttributes(\n                attrs, R.styleable.QMUIRadiusImageView, defStyleAttr, 0);\n\n        mBorderWidth = array.getDimensionPixelSize(R.styleable.QMUIRadiusImageView_qmui_border_width, 0);\n        mBorderColor = array.getColor(R.styleable.QMUIRadiusImageView_qmui_border_color, DEFAULT_BORDER_COLOR);\n        mSelectedBorderWidth = array.getDimensionPixelSize(\n                R.styleable.QMUIRadiusImageView_qmui_selected_border_width, mBorderWidth);\n        mSelectedBorderColor = array.getColor(\n                R.styleable.QMUIRadiusImageView_qmui_selected_border_color, mBorderColor);\n        mSelectedMaskColor = array.getColor(\n                R.styleable.QMUIRadiusImageView_qmui_selected_mask_color, Color.TRANSPARENT);\n        if (mSelectedMaskColor != Color.TRANSPARENT) {\n            mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);\n        }\n\n        mIsTouchSelectModeEnabled = array.getBoolean(\n                R.styleable.QMUIRadiusImageView_qmui_is_touch_select_mode_enabled, true);\n        mIsCircle = array.getBoolean(R.styleable.QMUIRadiusImageView_qmui_is_circle, false);\n        if (!mIsCircle) {\n            mIsOval = array.getBoolean(R.styleable.QMUIRadiusImageView_qmui_is_oval, false);\n        }\n        if (!mIsOval) {\n            mCornerRadius = array.getDimensionPixelSize(\n                    R.styleable.QMUIRadiusImageView_qmui_corner_radius, 0);\n        }\n        array.recycle();\n    }\n\n    @Override\n    public void setAdjustViewBounds(boolean adjustViewBounds) {\n        if (adjustViewBounds) {\n            throw new IllegalArgumentException(\"不支持adjustViewBounds\");\n        }\n    }\n\n    public void setBorderWidth(int borderWidth) {\n        if (mBorderWidth != borderWidth) {\n            mBorderWidth = borderWidth;\n            invalidate();\n        }\n    }\n\n    public void setBorderColor(@ColorInt int borderColor) {\n        if (mBorderColor != borderColor) {\n            mBorderColor = borderColor;\n            invalidate();\n        }\n    }\n\n    public void setCornerRadius(int cornerRadius) {\n        if (mCornerRadius != cornerRadius) {\n            mCornerRadius = cornerRadius;\n            if (!mIsCircle && !mIsOval) {\n                invalidate();\n            }\n        }\n    }\n\n    public void setSelectedBorderColor(@ColorInt int selectedBorderColor) {\n        if (mSelectedBorderColor != selectedBorderColor) {\n            mSelectedBorderColor = selectedBorderColor;\n            if (mIsSelected) {\n                invalidate();\n            }\n        }\n\n    }\n\n    public void setSelectedBorderWidth(int selectedBorderWidth) {\n        if (mSelectedBorderWidth != selectedBorderWidth) {\n            mSelectedBorderWidth = selectedBorderWidth;\n            if (mIsSelected) {\n                invalidate();\n            }\n        }\n    }\n\n    public void setSelectedMaskColor(@ColorInt int selectedMaskColor) {\n        if (mSelectedMaskColor != selectedMaskColor) {\n            mSelectedMaskColor = selectedMaskColor;\n            if (mSelectedMaskColor != Color.TRANSPARENT) {\n                mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);\n            } else {\n                mSelectedColorFilter = null;\n            }\n            if (mIsSelected) {\n                invalidate();\n            }\n        }\n        mSelectedMaskColor = selectedMaskColor;\n    }\n\n\n    public void setCircle(boolean isCircle) {\n        if (mIsCircle != isCircle) {\n            mIsCircle = isCircle;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public void setOval(boolean isOval) {\n        boolean forceUpdate = false;\n        if (isOval) {\n            if (mIsCircle) {\n                // 必须先取消圆形\n                mIsCircle = false;\n                forceUpdate = true;\n            }\n\n        }\n        if (mIsOval != isOval || forceUpdate) {\n            mIsOval = isOval;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public int getBorderColor() {\n        return mBorderColor;\n    }\n\n    public int getBorderWidth() {\n        return mBorderWidth;\n    }\n\n    public int getCornerRadius() {\n        return mCornerRadius;\n    }\n\n    public int getSelectedBorderColor() {\n        return mSelectedBorderColor;\n    }\n\n    public int getSelectedBorderWidth() {\n        return mSelectedBorderWidth;\n    }\n\n    public int getSelectedMaskColor() {\n        return mSelectedMaskColor;\n    }\n\n\n    public boolean isCircle() {\n        return mIsCircle;\n    }\n\n    public boolean isOval() {\n        return !mIsCircle && mIsOval;\n    }\n\n    public boolean isSelected() {\n        return mIsSelected;\n    }\n\n    public void setSelected(boolean isSelected) {\n        if (mIsSelected != isSelected) {\n            mIsSelected = isSelected;\n            invalidate();\n        }\n    }\n\n    public void setTouchSelectModeEnabled(boolean touchSelectModeEnabled) {\n        mIsTouchSelectModeEnabled = touchSelectModeEnabled;\n    }\n\n    public boolean isTouchSelectModeEnabled() {\n        return mIsTouchSelectModeEnabled;\n    }\n\n    public void setSelectedColorFilter(ColorFilter cf) {\n        if (mSelectedColorFilter == cf) {\n            return;\n        }\n        mSelectedColorFilter = cf;\n        if (mIsSelected) {\n            invalidate();\n        }\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n        if (mColorFilter == cf) {\n            return;\n        }\n        mColorFilter = cf;\n        if (!mIsSelected) {\n            invalidate();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {\n            setMeasuredDimension(widthSize, heightSize);\n            return;\n        }\n        if (mIsCircle) {\n            if (widthMode == MeasureSpec.EXACTLY) {\n                setMeasuredDimension(widthSize, widthSize);\n            } else if (heightMode == MeasureSpec.EXACTLY) {\n                setMeasuredDimension(heightSize, heightSize);\n            } else {\n                if (mBitmap == null) {\n                    setMeasuredDimension(0, 0);\n                } else {\n                    int w = Math.min(mBitmap.getWidth(), widthSize);\n                    int h = Math.min(mBitmap.getHeight(), heightSize);\n                    int size = Math.min(w, h);\n                    setMeasuredDimension(size, size);\n                }\n            }\n            return;\n        }\n\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n\n    @Override\n    public void setImageDrawable(Drawable drawable) {\n        super.setImageDrawable(drawable);\n        setupBitmap();\n    }\n\n    @Override\n    public void setImageURI(Uri uri) {\n        super.setImageURI(uri);\n        setupBitmap();\n    }\n\n    private Bitmap getBitmap() {\n        Drawable drawable = getDrawable();\n        if (drawable == null) {\n            return null;\n        }\n\n        if (drawable instanceof BitmapDrawable) {\n            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();\n            if (bitmap == null) {\n                return null;\n            }\n            float bmWidth = bitmap.getWidth(), bmHeight = bitmap.getHeight();\n            if (bmWidth == 0 || bmHeight == 0) {\n                return null;\n            }\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                // ensure minWidth and minHeight\n                float minScaleX = getMinimumWidth() / bmWidth, minScaleY = getMinimumHeight() / bmHeight;\n                if (minScaleX > 1 || minScaleY > 1) {\n                    float scale = Math.max(minScaleX, minScaleY);\n                    Matrix matrix = new Matrix();\n                    matrix.postScale(scale, scale);\n\n                    return Bitmap.createBitmap(\n                            bitmap, 0, 0, (int) bmWidth, (int) bmHeight, matrix, false);\n                }\n            }\n            return bitmap;\n        }\n\n        try {\n            Bitmap bitmap;\n\n            if (drawable instanceof ColorDrawable) {\n                bitmap = Bitmap.createBitmap(COLOR_DRAWABLE_DIMEN, COLOR_DRAWABLE_DIMEN, BITMAP_CONFIG);\n            } else {\n                bitmap = Bitmap.createBitmap(\n                        drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);\n            }\n\n            Canvas canvas = new Canvas(bitmap);\n            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n            drawable.draw(canvas);\n            return bitmap;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n\n    public void setupBitmap() {\n        Bitmap bm = getBitmap();\n        if (bm == mBitmap) {\n            return;\n        }\n        mBitmap = bm;\n        if (mBitmap == null) {\n            mBitmapShader = null;\n            invalidate();\n            return;\n        }\n        mNeedResetShader = true;\n        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);\n        if (mBitmapPaint == null) {\n            mBitmapPaint = new Paint();\n            mBitmapPaint.setAntiAlias(true);\n        }\n        mBitmapPaint.setShader(mBitmapShader);\n        requestLayout();\n        invalidate();\n    }\n\n    private void updateBitmapShader() {\n        mMatrix.reset();\n        mNeedResetShader = false;\n        if (mBitmapShader == null || mBitmap == null) {\n            return;\n        }\n        updateMatrix(mMatrix, mBitmap, mRectF);\n        mBitmapShader.setLocalMatrix(mMatrix);\n        mBitmapPaint.setShader(mBitmapShader);\n    }\n\n    private void updateMatrix(@NonNull Matrix matrix, @NonNull Bitmap bitmap, RectF drawRect) {\n        final float bmWidth = bitmap.getWidth();\n        final float bmHeight = bitmap.getHeight();\n        final ScaleType scaleType = getScaleType();\n        if (scaleType == ScaleType.MATRIX) {\n            updateScaleTypeMatrix(matrix, bitmap, drawRect);\n        } else if (scaleType == ScaleType.CENTER) {\n            float left = (mWidth - bmWidth) / 2;\n            float top = (mHeight - bmHeight) / 2;\n            matrix.postTranslate(left, top);\n            drawRect.set(\n                    Math.max(0, left),\n                    Math.max(0, top),\n                    Math.min(left + bmWidth, mWidth),\n                    Math.min(top + bmHeight, mHeight));\n        } else if (scaleType == ScaleType.CENTER_CROP) {\n            float scaleX = mWidth / bmWidth, scaleY = mHeight / bmHeight;\n            final float scale = Math.max(scaleX, scaleY);\n            matrix.setScale(scale, scale);\n            matrix.postTranslate(-(scale * bmWidth - mWidth) / 2, -(scale * bmHeight - mHeight) / 2);\n            drawRect.set(0, 0, mWidth, mHeight);\n        } else if (scaleType == ScaleType.CENTER_INSIDE) {\n            float scaleX = mWidth / bmWidth, scaleY = mHeight / bmHeight;\n            if (scaleX >= 1 && scaleY >= 1) {\n                float left = (mWidth - bmWidth) / 2;\n                float top = (mHeight - bmHeight) / 2;\n                matrix.postTranslate(left, top);\n                drawRect.set(left, top, left + bmWidth, top + bmHeight);\n            } else {\n                float scale = Math.min(scaleX, scaleY);\n                matrix.setScale(scale, scale);\n                float bw = bmWidth * scale, bh = bmHeight * scale;\n                float left = (mWidth - bw) / 2;\n                float top = (mHeight - bh) / 2;\n                matrix.postTranslate(left, top);\n                drawRect.set(left, top, left + bw, top + bh);\n            }\n        } else if (scaleType == ScaleType.FIT_XY) {\n            float scaleX = mWidth / bmWidth, scaleY = mHeight / bmHeight;\n            matrix.setScale(scaleX, scaleY);\n            drawRect.set(0, 0, mWidth, mHeight);\n        } else {\n            float scaleX = mWidth / bmWidth, scaleY = mHeight / bmHeight;\n            float scale = Math.min(scaleX, scaleY);\n            matrix.setScale(scale, scale);\n            float bw = bmWidth * scale, bh = bmHeight * scale;\n            if (scaleType == ScaleType.FIT_START) {\n                drawRect.set(0, 0, bw, bh);\n            } else if (scaleType == ScaleType.FIT_CENTER) {\n                float left = (mWidth - bw) / 2;\n                float top = (mHeight - bh) / 2;\n                matrix.postTranslate(left, top);\n                drawRect.set(left, top, left + bw, top + bh);\n            } else {\n                matrix.postTranslate(mWidth - bw, mHeight - bh);\n                drawRect.set(mWidth - bw, mHeight - bh, mWidth, mHeight);\n            }\n        }\n\n    }\n\n    protected void updateScaleTypeMatrix(@NonNull Matrix matrix, @NonNull Bitmap bitmap, RectF drawRect) {\n        matrix.set(getImageMatrix());\n        drawRect.set(0, 0, mWidth, mHeight);\n    }\n\n    private void drawBitmap(Canvas canvas, int borderWidth) {\n        final float halfBorderWidth = borderWidth * 1.0f / 2;\n        mBitmapPaint.setColorFilter(mIsSelected ? mSelectedColorFilter : mColorFilter);\n\n        if (mIsCircle) {\n            canvas.drawCircle(mRectF.centerX(), mRectF.centerY(), (Math.min(mRectF.width() / 2, mRectF.height() / 2)) - halfBorderWidth, mBitmapPaint);\n        } else {\n            mDrawRectF.left = mRectF.left + halfBorderWidth;\n            //noinspection SuspiciousNameCombination\n            mDrawRectF.top = mRectF.top + halfBorderWidth;\n            mDrawRectF.right = mRectF.right - halfBorderWidth;\n            mDrawRectF.bottom = mRectF.bottom - halfBorderWidth;\n            if (mIsOval) {\n                canvas.drawOval(mDrawRectF, mBitmapPaint);\n            } else {\n                canvas.drawRoundRect(mDrawRectF, mCornerRadius, mCornerRadius, mBitmapPaint);\n            }\n        }\n    }\n\n    private void drawBorder(Canvas canvas, int borderWidth) {\n        if (borderWidth <= 0) {\n            return;\n        }\n        final float halfBorderWidth = borderWidth * 1.0f / 2;\n        mBorderPaint.setColor(mIsSelected ? mSelectedBorderColor : mBorderColor);\n        mBorderPaint.setStrokeWidth(borderWidth);\n        if (mIsCircle) {\n            canvas.drawCircle(mRectF.centerX(), mRectF.centerY(),\n                    Math.min(mRectF.width(), mRectF.height()) / 2 - halfBorderWidth, mBorderPaint);\n        } else {\n            mDrawRectF.left = mRectF.left + halfBorderWidth;\n            //noinspection SuspiciousNameCombination\n            mDrawRectF.top = mRectF.top + halfBorderWidth;\n            mDrawRectF.right = mRectF.right - halfBorderWidth;\n            mDrawRectF.bottom = mRectF.bottom - halfBorderWidth;\n            if (mIsOval) {\n                canvas.drawOval(mDrawRectF, mBorderPaint);\n            } else {\n                canvas.drawRoundRect(mDrawRectF, mCornerRadius, mCornerRadius, mBorderPaint);\n            }\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        int width = getWidth(), height = getHeight();\n        if (width <= 0 || height <= 0) {\n            return;\n        }\n\n        int borderWidth = mIsSelected ? mSelectedBorderWidth : mBorderWidth;\n\n        if (mBitmap == null || mBitmapShader == null) {\n            drawBorder(canvas, borderWidth);\n            return;\n        }\n\n        if (mWidth != width || mHeight != height\n                || mLastCalculateScaleType != getScaleType() || mNeedResetShader) {\n            mWidth = width;\n            mHeight = height;\n            mLastCalculateScaleType = getScaleType();\n            updateBitmapShader();\n        }\n        drawBitmap(canvas, borderWidth);\n        drawBorder(canvas, borderWidth);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!this.isClickable()) {\n            this.setSelected(false);\n            return super.onTouchEvent(event);\n        }\n\n        if (!mIsTouchSelectModeEnabled) {\n            return super.onTouchEvent(event);\n        }\n        switch (event.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                this.setSelected(true);\n                break;\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_SCROLL:\n            case MotionEvent.ACTION_OUTSIDE:\n            case MotionEvent.ACTION_CANCEL:\n                this.setSelected(false);\n                break;\n        }\n        return super.onTouchEvent(event);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIRadiusImageView2.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffColorFilter;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.ColorInt;\nimport androidx.appcompat.widget.AppCompatImageView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaViewHelper;\nimport com.qmuiteam.qmui.layout.IQMUILayout;\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\n\n/**\n * shown image in radius view, is different to {@link QMUIRadiusImageView}\n * the oval is not supported\n */\npublic class QMUIRadiusImageView2 extends AppCompatImageView implements IQMUILayout {\n    private static final int DEFAULT_BORDER_COLOR = Color.GRAY;\n\n    private QMUILayoutHelper mLayoutHelper;\n    private QMUIAlphaViewHelper mAlphaViewHelper;\n    private boolean mIsCircle = false;\n    private boolean mIsSelected = false;\n\n    private int mBorderWidth;\n    private int mBorderColor;\n\n    private int mSelectedBorderWidth;\n    private int mSelectedBorderColor;\n    private int mSelectedMaskColor;\n    private boolean mIsTouchSelectModeEnabled = true;\n    private ColorFilter mColorFilter;\n    private ColorFilter mSelectedColorFilter;\n    private boolean mIsInOnTouchEvent = false;\n\n    public QMUIRadiusImageView2(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRadiusImageView2(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIRadiusImageView2(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        setChangeAlphaWhenPress(false);\n        setChangeAlphaWhenDisable(false);\n\n        TypedArray array = context.obtainStyledAttributes(\n                attrs, R.styleable.QMUIRadiusImageView2, defStyleAttr, 0);\n\n        mBorderWidth = array.getDimensionPixelSize(R.styleable.QMUIRadiusImageView2_qmui_border_width, 0);\n        mBorderColor = array.getColor(R.styleable.QMUIRadiusImageView2_qmui_border_color, DEFAULT_BORDER_COLOR);\n        mSelectedBorderWidth = array.getDimensionPixelSize(\n                R.styleable.QMUIRadiusImageView2_qmui_selected_border_width, mBorderWidth);\n        mSelectedBorderColor = array.getColor(\n                R.styleable.QMUIRadiusImageView2_qmui_selected_border_color, mBorderColor);\n        mSelectedMaskColor = array.getColor(\n                R.styleable.QMUIRadiusImageView2_qmui_selected_mask_color, Color.TRANSPARENT);\n        if (mSelectedMaskColor != Color.TRANSPARENT) {\n            mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);\n        }\n\n        mIsTouchSelectModeEnabled = array.getBoolean(\n                R.styleable.QMUIRadiusImageView2_qmui_is_touch_select_mode_enabled, true);\n        mIsCircle = array.getBoolean(R.styleable.QMUIRadiusImageView2_qmui_is_circle, false);\n        if (!mIsCircle) {\n            setRadius(array.getDimensionPixelSize(\n                    R.styleable.QMUIRadiusImageView2_qmui_corner_radius, 0));\n        }\n        array.recycle();\n\n        mLayoutHelper.setBorderWidth(mBorderWidth);\n        mLayoutHelper.setBorderColor(mBorderColor);\n    }\n\n\n    private QMUIAlphaViewHelper getAlphaViewHelper() {\n        if (mAlphaViewHelper == null) {\n            mAlphaViewHelper = new QMUIAlphaViewHelper(this);\n        }\n        return mAlphaViewHelper;\n    }\n\n    public void setCornerRadius(int cornerRadius) {\n        setRadius(cornerRadius);\n    }\n\n    @Override\n    protected boolean setFrame(int l, int t, int r, int b) {\n        return super.setFrame(l, t, r, b);\n    }\n\n    @Override\n    public void setPressed(boolean pressed) {\n        super.setPressed(pressed);\n        getAlphaViewHelper().onPressedChanged(this, pressed);\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        getAlphaViewHelper().onEnabledChanged(this, enabled);\n    }\n\n    /**\n     * 设置是否要在 press 时改变透明度\n     *\n     * @param changeAlphaWhenPress 是否要在 press 时改变透明度\n     */\n    public void setChangeAlphaWhenPress(boolean changeAlphaWhenPress) {\n        getAlphaViewHelper().setChangeAlphaWhenPress(changeAlphaWhenPress);\n    }\n\n    /**\n     * 设置是否要在 disabled 时改变透明度\n     *\n     * @param changeAlphaWhenDisable 是否要在 disabled 时改变透明度\n     */\n    public void setChangeAlphaWhenDisable(boolean changeAlphaWhenDisable) {\n        getAlphaViewHelper().setChangeAlphaWhenDisable(changeAlphaWhenDisable);\n    }\n\n    public void setCircle(boolean isCircle) {\n        if (mIsCircle != isCircle) {\n            mIsCircle = isCircle;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public int getBorderColor() {\n        return mBorderColor;\n    }\n\n    public int getBorderWidth() {\n        return mBorderWidth;\n    }\n\n    public int getCornerRadius() {\n        return getRadius();\n    }\n\n    public int getSelectedBorderColor() {\n        return mSelectedBorderColor;\n    }\n\n    public int getSelectedBorderWidth() {\n        return mSelectedBorderWidth;\n    }\n\n    public int getSelectedMaskColor() {\n        return mSelectedMaskColor;\n    }\n\n\n    public boolean isCircle() {\n        return mIsCircle;\n    }\n\n    public boolean isSelected() {\n        return mIsSelected;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n        if (mIsCircle) {\n            int h = getMeasuredHeight();\n            int w = getMeasuredWidth();\n            int radius = w / 2;\n            if (h != w) {\n                int size = Math.min(h, w);\n                radius = size / 2;\n                int measureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);\n                super.onMeasure(measureSpec, measureSpec);\n            }\n            setRadius(radius);\n        }\n    }\n\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        if (mBorderColor != borderColor) {\n            mBorderColor = borderColor;\n            if (!mIsSelected) {\n                mLayoutHelper.setBorderColor(borderColor);\n                invalidate();\n            }\n        }\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        if (mBorderWidth != borderWidth) {\n            mBorderWidth = borderWidth;\n            if (!mIsSelected) {\n                mLayoutHelper.setBorderWidth(borderWidth);\n                invalidate();\n            }\n        }\n    }\n\n    public void setSelectedBorderColor(@ColorInt int selectedBorderColor) {\n        if (mSelectedBorderColor != selectedBorderColor) {\n            mSelectedBorderColor = selectedBorderColor;\n            if (mIsSelected) {\n                mLayoutHelper.setBorderColor(selectedBorderColor);\n                invalidate();\n            }\n        }\n\n    }\n\n    public void setSelectedBorderWidth(int selectedBorderWidth) {\n        if (mSelectedBorderWidth != selectedBorderWidth) {\n            mSelectedBorderWidth = selectedBorderWidth;\n            if (mIsSelected) {\n                mLayoutHelper.setBorderWidth(selectedBorderWidth);\n                invalidate();\n            }\n        }\n    }\n\n    public void setSelectedMaskColor(@ColorInt int selectedMaskColor) {\n        if (mSelectedMaskColor != selectedMaskColor) {\n            mSelectedMaskColor = selectedMaskColor;\n            if (mSelectedMaskColor != Color.TRANSPARENT) {\n                mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);\n            } else {\n                mSelectedColorFilter = null;\n            }\n            if (mIsSelected) {\n                invalidate();\n            }\n        }\n        mSelectedMaskColor = selectedMaskColor;\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public void setSelected(boolean selected) {\n        if (!mIsInOnTouchEvent) {\n            super.setSelected(selected);\n        }\n        if (mIsSelected != selected) {\n            mIsSelected = selected;\n            if (mIsSelected) {\n                super.setColorFilter(mSelectedColorFilter);\n            } else {\n                super.setColorFilter(mColorFilter);\n            }\n            int borderWidth = mIsSelected ? mSelectedBorderWidth : mBorderWidth;\n            int borderColor = mIsSelected ? mSelectedBorderColor : mBorderColor;\n            mLayoutHelper.setBorderWidth(borderWidth);\n            mLayoutHelper.setBorderColor(borderColor);\n            invalidate();\n        }\n    }\n\n    public void setTouchSelectModeEnabled(boolean touchSelectModeEnabled) {\n        mIsTouchSelectModeEnabled = touchSelectModeEnabled;\n    }\n\n    public boolean isTouchSelectModeEnabled() {\n        return mIsTouchSelectModeEnabled;\n    }\n\n    public void setSelectedColorFilter(ColorFilter cf) {\n        if (mSelectedColorFilter == cf) {\n            return;\n        }\n        mSelectedColorFilter = cf;\n        if (mIsSelected) {\n            super.setColorFilter(cf);\n        }\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n        if (mColorFilter == cf) {\n            return;\n        }\n        mColorFilter = cf;\n        if (!mIsSelected) {\n            super.setColorFilter(cf);\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!this.isClickable()) {\n            return super.onTouchEvent(event);\n        } else if (mIsTouchSelectModeEnabled) {\n            mIsInOnTouchEvent = true;\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    this.setSelected(true);\n                    break;\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_SCROLL:\n                case MotionEvent.ACTION_OUTSIDE:\n                case MotionEvent.ACTION_CANCEL:\n                    this.setSelected(false);\n                    break;\n            }\n            mIsInOnTouchEvent = false;\n        }\n\n        return super.onTouchEvent(event);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUISeekBar.java",
    "content": "package com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\n\npublic class QMUISeekBar extends QMUISlider {\n    private int mTickHeight;\n    private int mTickWidth;\n\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(2);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_seek_bar_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.PROGRESS_COLOR, R.attr.qmui_skin_support_seek_bar_color);\n    }\n\n    public QMUISeekBar(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QMUISeekBar(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUISeekBarStyle);\n    }\n\n    public QMUISeekBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = getContext().obtainStyledAttributes(attrs,\n                R.styleable.QMUISeekBar, defStyleAttr, 0);\n        mTickWidth = array.getDimensionPixelSize(R.styleable.QMUISeekBar_qmui_seek_bar_tick_width,\n                QMUIDisplayHelper.dp2px(context, 1));\n        mTickHeight = array.getDimensionPixelSize(R.styleable.QMUISeekBar_qmui_seek_bar_tick_height,\n                QMUIDisplayHelper.dp2px(context, 4));\n        array.recycle();\n        setClickToChangeProgress(true);\n    }\n\n    public void setTickHeight(int tickHeight) {\n        mTickHeight = tickHeight;\n        invalidate();\n    }\n\n    public void setTickWidth(int tickWidth) {\n        mTickWidth = tickWidth;\n        invalidate();\n    }\n\n    public int getTickHeight() {\n        return mTickHeight;\n    }\n\n    @Override\n    protected void drawRect(Canvas canvas, RectF rect, int barHeight, Paint paint, boolean forProgress) {\n        canvas.drawRect(rect, paint);\n    }\n\n    @Override\n    protected void drawTick(Canvas canvas, int currentTickCount, int totalTickCount,\n                            int left, int right, float y,\n                            Paint paint, int barNormalColor, int barProgressColor) {\n        if (mTickHeight <= 0 || mTickWidth <= 0 || totalTickCount < 1) {\n            return;\n        }\n        float step = ((float) (right - left - mTickWidth)) / totalTickCount;\n        float t = y - mTickHeight / 2f;\n        float b = y + mTickHeight / 2f;\n        float l, r;\n        float x = left + mTickWidth / 2f;\n        for (int i = 0; i <= totalTickCount; i++) {\n            l = x - mTickWidth / 2f;\n            r = x + mTickWidth / 2f;\n            paint.setColor(i <= currentTickCount ? barProgressColor : barNormalColor);\n            paint.setStyle(Paint.Style.FILL);\n            canvas.drawRect(l, t, r, b, paint);\n            x += step;\n        }\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUISlider.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.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.RectF;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\n\npublic class QMUISlider extends FrameLayout implements IQMUISkinDefaultAttrProvider {\n    public static final int PROGRESS_NOT_SET = -1;\n    private Paint mBarPaint;\n    private int mBarHeight;\n    private int mBarNormalColor;\n    private int mBarProgressColor;\n    private int mRecordProgressColor;\n    private boolean mConstraintThumbInMoving = true;\n    private Callback mCallback;\n    private IThumbView mThumbView;\n    private QMUIViewOffsetHelper mThumbViewOffsetHelper;\n\n    private int mTickCount;\n    private int mCurrentProgress = 0;\n    private boolean mIsProgressFirstSet = false;\n    private boolean mClickToChangeProgress = false;\n    private boolean mLongTouchToChangeProgress = false;\n    private int mRecordProgress = PROGRESS_NOT_SET;\n\n    private int mDownTouchX = 0;\n    private int mLastTouchX = 0;\n    private boolean mIsThumbTouched = false;\n    private boolean mIsMoving = false;\n    private int mTouchSlop;\n    private RectF mTempRect = new RectF();\n    private LongPressAction mLongPressAction = new LongPressAction();\n\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(2);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_slider_bar_bg_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.PROGRESS_COLOR, R.attr.qmui_skin_support_slider_bar_progress_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.HINT_COLOR, R.attr.qmui_skin_support_slider_record_progress_color);\n    }\n\n\n    public QMUISlider(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QMUISlider(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUISliderStyle);\n    }\n\n    public QMUISlider(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = getContext().obtainStyledAttributes(attrs,\n                R.styleable.QMUISlider, defStyleAttr, 0);\n        mBarHeight = array.getDimensionPixelSize(R.styleable.QMUISlider_qmui_slider_bar_height,\n                QMUIDisplayHelper.dp2px(context, 2));\n        mBarNormalColor = array.getColor(R.styleable.QMUISlider_qmui_slider_bar_normal_color, Color.WHITE);\n        mBarProgressColor = array.getColor(R.styleable.QMUISlider_qmui_slider_bar_progress_color, Color.BLUE);\n        mRecordProgressColor = array.getColor(R.styleable.QMUISlider_qmui_slider_bar_record_progress_color, Color.GRAY);\n        mTickCount = array.getInt(R.styleable.QMUISlider_qmui_slider_bar_tick_count, 100);\n        mConstraintThumbInMoving = array.getBoolean(R.styleable.QMUISlider_qmui_slider_bar_constraint_thumb_in_moving, true);\n        int thumbSize = array.getDimensionPixelSize(\n                R.styleable.QMUISlider_qmui_slider_bar_thumb_size,\n                QMUIDisplayHelper.dp2px(getContext(), 24));\n        int thumbStyleAttr = 0;\n        String thumbStyleAttrString = array.getString(R.styleable.QMUISlider_qmui_slider_bar_thumb_style_attr);\n        if (thumbStyleAttrString != null) {\n            thumbStyleAttr = getResources().getIdentifier(\n                    thumbStyleAttrString, \"attr\", context.getPackageName());\n        }\n\n        boolean useClipChildrenByDeveloper = array.getBoolean(\n                R.styleable.QMUISlider_qmui_slider_bar_use_clip_children_by_developer, false);\n        if (!useClipChildrenByDeveloper) {\n            int paddingHor = array.getDimensionPixelOffset(\n                    R.styleable.QMUISlider_qmui_slider_bar_padding_hor_for_thumb_shadow, 0);\n            int paddingVer = array.getDimensionPixelOffset(\n                    R.styleable.QMUISlider_qmui_slider_bar_padding_ver_for_thumb_shadow, 0);\n            setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n        }\n        array.recycle();\n        mBarPaint = new Paint();\n        mBarPaint.setStyle(Paint.Style.FILL);\n        mBarPaint.setAntiAlias(true);\n        mTouchSlop = QMUIDisplayHelper.dp2px(context, 2);\n        setWillNotDraw(false);\n        setClipToPadding(false);\n        setClipChildren(false);\n        IThumbView thumbView = onCreateThumbView(context, thumbSize, thumbStyleAttr);\n        if (!(thumbView instanceof View)) {\n            throw new IllegalArgumentException(\"thumbView must be a instance of View\");\n        }\n        mThumbView = thumbView;\n        View thumbAsView = (View) thumbView;\n        mThumbViewOffsetHelper = new QMUIViewOffsetHelper(thumbAsView);\n        addView(thumbAsView, onCreateThumbLayoutParams());\n        thumbView.render(mCurrentProgress, mTickCount);\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    public void setCurrentProgress(int currentProgress) {\n        if (!mIsMoving) {\n            int progress = QMUILangHelper.constrain(currentProgress, 0, mTickCount);\n            if (mCurrentProgress != progress || !mIsProgressFirstSet) {\n                mIsProgressFirstSet = true;\n                safeSetCurrentProgress(progress);\n                if (mCallback != null) {\n                    mCallback.onProgressChange(this, progress, mTickCount, false);\n                }\n                invalidate();\n            }\n        }\n    }\n\n    public void setRecordProgress(int recordProgress) {\n        if (recordProgress != mRecordProgress) {\n            if (recordProgress != PROGRESS_NOT_SET) {\n                recordProgress = QMUILangHelper.constrain(recordProgress, 0, mTickCount);\n            }\n            mRecordProgress = recordProgress;\n            invalidate();\n        }\n    }\n\n    public int getCurrentProgress() {\n        return mCurrentProgress;\n    }\n\n    public void setTickCount(int tickCount) {\n        if (mTickCount != tickCount) {\n            mTickCount = tickCount;\n            setCurrentProgress(QMUILangHelper.constrain(mCurrentProgress, 0, mTickCount));\n            mThumbView.render(mCurrentProgress, mTickCount);\n            invalidate();\n        }\n    }\n\n    public int getTickCount() {\n        return mTickCount;\n    }\n\n    public void setThumbSkin(QMUISkinValueBuilder valueBuilder) {\n        QMUISkinHelper.setSkinValue(convertThumbToView(), valueBuilder);\n    }\n\n    private void safeSetCurrentProgress(int currentProgress) {\n        mCurrentProgress = currentProgress;\n        mThumbView.render(currentProgress, mTickCount);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        if (getMeasuredHeight() < mBarHeight) {\n            super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                    mBarHeight + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY));\n        }\n    }\n\n    @Override\n    protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        onLayoutCustomChildren(changed, left, top, right, bottom);\n        View thumbView = convertThumbToView();\n        int paddingTop = getPaddingTop(),\n                thumbHeight = thumbView.getMeasuredHeight(),\n                thumbWidth = thumbView.getMeasuredWidth();\n        int l = getPaddingLeft() + mThumbView.getLeftRightMargin();\n        int t = paddingTop +\n                (bottom - top - paddingTop - getPaddingBottom() - thumbView.getMeasuredHeight()) / 2;\n        thumbView.layout(l, t, l + thumbWidth, t + thumbHeight);\n        mThumbViewOffsetHelper.onViewLayout();\n    }\n\n    protected void onLayoutCustomChildren(boolean changed, int left, int top, int right, int bottom) {\n\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        return true;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!isEnabled()) {\n            return false;\n        }\n\n        int action = event.getAction();\n        if (action == MotionEvent.ACTION_DOWN) {\n            mDownTouchX = (int) event.getX();\n            mLastTouchX = mDownTouchX;\n            mIsThumbTouched = isThumbTouched(event.getX(), event.getY());\n            if (mIsThumbTouched) {\n                mThumbView.setPress(true);\n            }else if(mLongTouchToChangeProgress){\n                removeCallbacks(mLongPressAction);\n                postOnAnimationDelayed(mLongPressAction, 300);\n            }\n\n            if (mCallback != null) {\n                mCallback.onTouchDown(this, mCurrentProgress, mTickCount, mIsThumbTouched);\n            }\n\n        } else if (action == MotionEvent.ACTION_MOVE) {\n            int x = (int) event.getX();\n            int dx = x - mLastTouchX;\n            mLastTouchX = x;\n            if (!mIsMoving && mIsThumbTouched) {\n                if (Math.abs(mLastTouchX - mDownTouchX) > mTouchSlop) {\n                    removeCallbacks(mLongPressAction);\n                    mIsMoving = true;\n                    if (mCallback != null) {\n                        mCallback.onStartMoving(this, mCurrentProgress, mTickCount);\n                    }\n                    if (dx > 0) {\n                        dx -= mTouchSlop;\n                    } else {\n                        dx += mTouchSlop;\n                    }\n                }\n            }\n\n            if (mIsMoving) {\n                QMUIViewHelper.safeRequestDisallowInterceptTouchEvent(this, true);\n                int maxOffset = getMaxThumbOffset();\n\n                int oldProgress = mCurrentProgress;\n                if (mConstraintThumbInMoving) {\n                    checkTouch(x, maxOffset);\n                } else {\n                    mThumbViewOffsetHelper.setLeftAndRightOffset(\n                            QMUILangHelper.constrain(\n                                    mThumbViewOffsetHelper.getLeftAndRightOffset() + dx,\n                                    0,\n                                    maxOffset)\n                    );\n                    calculateByThumbPosition(maxOffset);\n                }\n                if (mCallback != null && oldProgress != mCurrentProgress) {\n                    mCallback.onProgressChange(this, mCurrentProgress, mTickCount, true);\n                }\n                invalidate();\n            }\n        } else if (action == MotionEvent.ACTION_UP ||\n                action == MotionEvent.ACTION_CANCEL) {\n            removeCallbacks(mLongPressAction);\n            mLastTouchX = -1;\n            QMUIViewHelper.safeRequestDisallowInterceptTouchEvent(this, false);\n            if (mIsMoving) {\n                mIsMoving = false;\n                if (mCallback != null) {\n                    mCallback.onStopMoving(this, mCurrentProgress, mTickCount);\n                }\n            }\n\n            if (mIsThumbTouched) {\n                mIsThumbTouched = false;\n                mThumbView.setPress(false);\n            } else if (action == MotionEvent.ACTION_UP) {\n                int x = (int) event.getX();\n                boolean isRecordProgressClicked = isRecordProgressClicked(x);\n                if (Math.abs(x - mDownTouchX) < mTouchSlop && (mClickToChangeProgress || isRecordProgressClicked)) {\n                    int oldProgress = mCurrentProgress;\n                    if (isRecordProgressClicked) {\n                        safeSetCurrentProgress(mRecordProgress);\n                    } else {\n                        checkTouch(x, getMaxThumbOffset());\n                    }\n                    invalidate();\n                    if (mCallback != null && oldProgress != mCurrentProgress) {\n                        mCallback.onProgressChange(this, mCurrentProgress, mTickCount, true);\n                    }\n                }\n\n            }\n            if (mCallback != null) {\n                mCallback.onTouchUp(this, mCurrentProgress, mTickCount);\n            }\n        } else {\n            removeCallbacks(mLongPressAction);\n        }\n\n        return true;\n    }\n\n    private void checkTouch(int touchX, int maxOffset) {\n        if(mThumbView == null){\n            return;\n        }\n        int moveX = touchX - getPaddingLeft() - mThumbView.getLeftRightMargin();\n        float step = (float) maxOffset / mTickCount;\n        if (moveX <= step / 2) {\n            mThumbViewOffsetHelper.setLeftAndRightOffset(0);\n            safeSetCurrentProgress(0);\n        } else if (touchX >= getWidth() - getPaddingRight() - mThumbView.getLeftRightMargin() - step / 2) {\n            mThumbViewOffsetHelper.setLeftAndRightOffset(maxOffset);\n            safeSetCurrentProgress(mTickCount);\n        } else {\n            float percent = (float) moveX / (getWidth() - getPaddingLeft() - getPaddingRight() - 2 * mThumbView.getLeftRightMargin());\n            int target = (int) (mTickCount * percent + 0.5f);\n            mThumbViewOffsetHelper.setLeftAndRightOffset((int) (target * step));\n            safeSetCurrentProgress(target);\n        }\n    }\n\n\n    public void setClickToChangeProgress(boolean clickToChangeProgress) {\n        mClickToChangeProgress = clickToChangeProgress;\n    }\n\n    public void setLongTouchToChangeProgress(boolean longTouchToChangeProgress) {\n        mLongTouchToChangeProgress = longTouchToChangeProgress;\n    }\n\n    public boolean isLongTouchToChangeProgress() {\n        return mLongTouchToChangeProgress;\n    }\n\n    public boolean isClickToChangeProgress() {\n        return mClickToChangeProgress;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        int l = getPaddingLeft();\n        int r = getWidth() - getPaddingRight();\n        int bt = getPaddingTop() + (getHeight() - getPaddingTop() - getPaddingBottom() - mBarHeight) / 2;\n        int bb = bt + mBarHeight;\n        mBarPaint.setColor(mBarNormalColor);\n        mTempRect.set(l, bt, r, bb);\n        drawRect(canvas, mTempRect, mBarHeight, mBarPaint, false);\n\n        float step = (float) getMaxThumbOffset() / mTickCount;\n        int progressOffset = (int) (step * mCurrentProgress);\n        mBarPaint.setColor(mBarProgressColor);\n\n        View thumb = convertThumbToView();\n        if (thumb != null && thumb.getVisibility() == View.VISIBLE) {\n            if (!mIsMoving) {\n                mThumbViewOffsetHelper.setLeftAndRightOffset(progressOffset);\n            }\n            mTempRect.set(l, bt, (thumb.getRight() + thumb.getLeft()) / 2f, bb);\n            drawRect(canvas, mTempRect, mBarHeight, mBarPaint, true);\n        } else {\n            mTempRect.set(l, bt, l + progressOffset, bb);\n            drawRect(canvas, mTempRect, mBarHeight, mBarPaint, true);\n        }\n\n        drawTick(canvas, mCurrentProgress, mTickCount, l, r, mTempRect.centerY(), mBarPaint, mBarNormalColor, mBarProgressColor);\n        if (mRecordProgress != PROGRESS_NOT_SET && thumb != null) {\n            mBarPaint.setColor(mRecordProgressColor);\n            float recordPos = getPaddingLeft() + mThumbView.getLeftRightMargin() + (int) (step * mRecordProgress);\n            mTempRect.set(recordPos, thumb.getTop(), recordPos + thumb.getWidth(), thumb.getBottom());\n            drawRecordProgress(canvas, mTempRect, mBarPaint);\n        }\n\n    }\n\n    protected void drawRect(Canvas canvas, RectF rect, int barHeight, Paint paint, boolean forProgress) {\n        int radius = barHeight / 2;\n        canvas.drawRoundRect(rect, radius, radius, paint);\n    }\n\n    protected void drawRecordProgress(Canvas canvas, RectF rect, Paint paint) {\n        float radius = rect.height() / 2;\n        canvas.drawRoundRect(rect, radius, radius, paint);\n    }\n\n    protected void drawTick(Canvas canvas, int currentTickCount, int totalTickCount,\n                            int left, int right, float y,\n                            Paint paint, int barNormalColor, int barProgressColor) {\n    }\n\n    public void setBarHeight(int barHeight) {\n        if (mBarHeight != barHeight) {\n            mBarHeight = barHeight;\n            requestLayout();\n        }\n    }\n\n    public int getBarHeight() {\n        return mBarHeight;\n    }\n\n    public void setBarNormalColor(int barNormalColor) {\n        if (mBarNormalColor != barNormalColor) {\n            mBarNormalColor = barNormalColor;\n            invalidate();\n        }\n    }\n\n    public int getBarNormalColor() {\n        return mBarNormalColor;\n    }\n\n    public void setBarProgressColor(int barProgressColor) {\n        if (mBarProgressColor != barProgressColor) {\n            mBarProgressColor = barProgressColor;\n            invalidate();\n        }\n    }\n\n    public int getBarProgressColor() {\n        return mBarProgressColor;\n    }\n\n    public void setRecordProgressColor(int recordProgressColor) {\n        if (mRecordProgressColor != recordProgressColor) {\n            mRecordProgressColor = recordProgressColor;\n            invalidate();\n        }\n    }\n\n    public int getRecordProgressColor() {\n        return mRecordProgressColor;\n    }\n\n    public int getRecordProgress() {\n        return mRecordProgress;\n    }\n\n    public void setConstraintThumbInMoving(boolean constraintThumbInMoving) {\n        mConstraintThumbInMoving = constraintThumbInMoving;\n    }\n\n    private void calculateByThumbPosition(int maxOffset) {\n        View thumbView = convertThumbToView();\n        float percent = mThumbViewOffsetHelper.getLeftAndRightOffset() * 1f / maxOffset;\n        safeSetCurrentProgress(QMUILangHelper.constrain(\n                (int) (mTickCount * percent + 0.5f),\n                0,\n                mTickCount\n        ));\n    }\n\n    @NonNull\n    protected IThumbView onCreateThumbView(Context context, int thumbSize, int thumbStyleAttr) {\n        return new DefaultThumbView(context, thumbSize, thumbStyleAttr);\n    }\n\n    protected LayoutParams onCreateThumbLayoutParams() {\n        return new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    private View convertThumbToView() {\n        return (View) mThumbView;\n    }\n\n    private boolean isThumbTouched(float x, float y) {\n        return isThumbViewTouched(convertThumbToView(), x, y);\n    }\n\n    protected boolean isThumbViewTouched(View thumbView, float x, float y) {\n        return thumbView.getVisibility() == View.VISIBLE &&\n                thumbView.getLeft() <= x && thumbView.getRight() >= x &&\n                thumbView.getTop() <= y && thumbView.getBottom() >= y;\n    }\n\n    protected boolean isRecordProgressClicked(int x) {\n        if (mRecordProgress == PROGRESS_NOT_SET) {\n            return false;\n        }\n        View thumbView = convertThumbToView();\n        float percent = mRecordProgress * 1f / mTickCount;\n        float left = (getWidth() - getPaddingLeft() - getPaddingRight()) * percent - thumbView.getWidth() / 2f;\n        float right = left + thumbView.getWidth();\n        return x >= left && x <= right;\n    }\n\n    private int getMaxThumbOffset() {\n        return getWidth() - getPaddingLeft() - getPaddingRight()\n                - mThumbView.getLeftRightMargin() * 2\n                - convertThumbToView().getWidth();\n    }\n\n    public interface IThumbView {\n        void render(int progress, int tickCount);\n\n        void setPress(boolean isPressed);\n\n        int getLeftRightMargin();\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n\n    public interface Callback {\n\n        void onProgressChange(QMUISlider slider, int progress, int tickCount, boolean fromUser);\n\n        void onTouchDown(QMUISlider slider, int progress, int tickCount, boolean hitThumb);\n\n        void onTouchUp(QMUISlider slider, int progress, int tickCount);\n\n        void onStartMoving(QMUISlider slider, int progress, int tickCount);\n\n        void onStopMoving(QMUISlider slider, int progress, int tickCount);\n\n        void onLongTouch(QMUISlider slider, int progress, int tickCount);\n    }\n\n    public static class DefaultCallback implements Callback {\n\n        @Override\n        public void onProgressChange(QMUISlider slider, int progress, int tickCount, boolean fromUser) {\n\n        }\n\n        @Override\n        public void onTouchDown(QMUISlider slider, int progress, int tickCount, boolean hitThumb) {\n\n        }\n\n        @Override\n        public void onTouchUp(QMUISlider slider, int progress, int tickCount) {\n\n        }\n\n        @Override\n        public void onStartMoving(QMUISlider slider, int progress, int tickCount) {\n\n        }\n\n        @Override\n        public void onStopMoving(QMUISlider slider, int progress, int tickCount) {\n\n        }\n\n        @Override\n        public void onLongTouch(QMUISlider slider, int progress, int tickCount) {\n\n        }\n    }\n\n\n    public static class DefaultThumbView extends View implements IThumbView, IQMUISkinDefaultAttrProvider {\n\n        private final QMUILayoutHelper mLayoutHelper;\n        private final int mSize;\n        private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n        static {\n            sDefaultSkinAttrs = new SimpleArrayMap<>(2);\n            sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_slider_thumb_bg_color);\n            sDefaultSkinAttrs.put(QMUISkinValueBuilder.BORDER, R.attr.qmui_skin_support_slider_thumb_border_color);\n        }\n\n        public DefaultThumbView(Context context, int size, int defAttr) {\n            super(context, null, defAttr);\n            mSize = size;\n            mLayoutHelper = new QMUILayoutHelper(context, null, defAttr, this);\n            mLayoutHelper.setRadius(size / 2);\n            setPress(false);\n        }\n\n        @Override\n        protected void dispatchDraw(Canvas canvas) {\n            super.dispatchDraw(canvas);\n            mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n            mLayoutHelper.dispatchRoundBorderDraw(canvas);\n        }\n\n        public void setBorderColor(int color) {\n            mLayoutHelper.setBorderColor(color);\n            invalidate();\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            setMeasuredDimension(mSize, mSize);\n        }\n\n\n        @Override\n        public void render(int progress, int tickCount) {\n\n        }\n\n        @Override\n        public void setPress(boolean isPressed) {\n\n        }\n\n        @Override\n        public int getLeftRightMargin() {\n            return 0;\n        }\n\n        @Override\n        public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n            return sDefaultSkinAttrs;\n        }\n    }\n\n    class LongPressAction implements Runnable {\n\n        @Override\n        public void run() {\n            mIsMoving = true;\n            int oldProgress = mCurrentProgress;\n            checkTouch(mLastTouchX, getMaxThumbOffset());\n            mIsThumbTouched = true;\n            mThumbView.setPress(true);\n            if (mCallback != null && oldProgress != mCurrentProgress) {\n                mCallback.onLongTouch(QMUISlider.this, mCurrentProgress, mTickCount);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUITopBar.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.text.TextUtils.TruncateAt;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaImageButton;\nimport com.qmuiteam.qmui.layout.QMUIRelativeLayout;\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerView;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.skin.defaultAttr.QMUISkinSimpleDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A standard toolbar for use within application content.\n * <p>\n * <ul>\n * <li>add icon/text/custom-view in left or right.</li>\n * <li>set title and subtitle with gravity support.</li>\n * </ul>\n */\npublic class QMUITopBar extends QMUIRelativeLayout implements IQMUISkinHandlerView, IQMUISkinDefaultAttrProvider {\n\n    private static final int DEFAULT_VIEW_ID = -1;\n    private int mLeftLastViewId; // 左侧最右 view 的 id\n    private int mRightLastViewId; // 右侧最左 view 的 id\n\n    private View mCenterView; // 中间的 View\n    private LinearLayout mTitleContainerView; // 包裹 title 和 subTitle 的容器\n    private QMUIQQFaceView mTitleView; // 显示 title 文字的 TextView\n    private QMUISpanTouchFixTextView mSubTitleView; // 显示 subTitle 文字的 TextView\n\n    private List<View> mLeftViewList;\n    private List<View> mRightViewList;\n    private int mTitleGravity;\n    private int mLeftBackDrawableRes;\n    private int mLeftBackViewWidth;\n    private boolean mClearLeftPaddingWhenAddLeftBackView;\n    private int mTitleTextSize;\n    private Typeface mTitleTypeface;\n    private Typeface mSubTitleTypeface;\n    private int mTitleTextSizeWithSubTitle;\n    private int mSubTitleTextSize;\n    private int mTitleTextColor;\n    private int mSubTitleTextColor;\n    private int mTitleMarginHorWhenNoBtnAside;\n    private int mTitleContainerPaddingHor;\n    private int mTopBarImageBtnWidth;\n    private int mTopBarImageBtnHeight;\n    private int mTopBarTextBtnPaddingHor;\n    private ColorStateList mTopBarTextBtnTextColor;\n    private int mTopBarTextBtnTextSize;\n    private Typeface mTopBarTextBtnTypeface;\n    private int mTopBarHeight = -1;\n    private Rect mTitleContainerRect;\n    private boolean mIsBackgroundSetterDisabled = false;\n    private TruncateAt mEllipsize;\n\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(4);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BOTTOM_SEPARATOR, R.attr.qmui_skin_support_topbar_separator_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_topbar_bg);\n    }\n\n    public QMUITopBar(Context context) {\n        this(context, null);\n    }\n\n    public QMUITopBar(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUITopBarStyle);\n    }\n\n    public QMUITopBar(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        initVar();\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void initVar() {\n        mLeftLastViewId = DEFAULT_VIEW_ID;\n        mRightLastViewId = DEFAULT_VIEW_ID;\n        mLeftViewList = new ArrayList<>();\n        mRightViewList = new ArrayList<>();\n    }\n\n    void init(Context context, AttributeSet attrs) {\n        init(context, attrs, R.attr.QMUITopBarStyle);\n    }\n\n    void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.QMUITopBar, defStyleAttr, 0);\n        mLeftBackDrawableRes = array.getResourceId(R.styleable.QMUITopBar_qmui_topbar_left_back_drawable_id, R.drawable.qmui_icon_topbar_back);\n        mLeftBackViewWidth = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_left_back_width, -1);\n        mClearLeftPaddingWhenAddLeftBackView = array.getBoolean(R.styleable.QMUITopBar_qmui_topbar_clear_left_padding_when_add_left_back_view, false);\n        mTitleGravity = array.getInt(R.styleable.QMUITopBar_qmui_topbar_title_gravity, Gravity.CENTER);\n        mTitleTextSize = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_title_text_size, QMUIDisplayHelper.sp2px(context, 17));\n        mTitleTextSizeWithSubTitle = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_title_text_size_with_subtitle, QMUIDisplayHelper.sp2px(context, 16));\n        mSubTitleTextSize = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_subtitle_text_size, QMUIDisplayHelper.sp2px(context, 11));\n        mTitleTextColor = array.getColor(R.styleable.QMUITopBar_qmui_topbar_title_color, QMUIResHelper.getAttrColor(context, R.attr.qmui_config_color_gray_1));\n        mSubTitleTextColor = array.getColor(R.styleable.QMUITopBar_qmui_topbar_subtitle_color, QMUIResHelper.getAttrColor(context, R.attr.qmui_config_color_gray_4));\n        mTitleMarginHorWhenNoBtnAside = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_title_margin_horizontal_when_no_btn_aside, 0);\n        mTitleContainerPaddingHor = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_title_container_padding_horizontal, 0);\n        mTopBarImageBtnWidth = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_image_btn_width, QMUIDisplayHelper.dp2px(context, 48));\n        mTopBarImageBtnHeight = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_image_btn_height, QMUIDisplayHelper.dp2px(context, 48));\n        mTopBarTextBtnPaddingHor = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_text_btn_padding_horizontal, QMUIDisplayHelper.dp2px(context, 12));\n        mTopBarTextBtnTextColor = array.getColorStateList(R.styleable.QMUITopBar_qmui_topbar_text_btn_color_state_list);\n        mTopBarTextBtnTextSize = array.getDimensionPixelSize(R.styleable.QMUITopBar_qmui_topbar_text_btn_text_size, QMUIDisplayHelper.sp2px(context, 16));\n\n        mTitleTypeface = array.getBoolean(R.styleable.QMUITopBar_qmui_topbar_title_bold, false) ? Typeface.DEFAULT_BOLD : null;\n        mSubTitleTypeface = array.getBoolean(R.styleable.QMUITopBar_qmui_topbar_subtitle_bold, false) ? Typeface.DEFAULT_BOLD : null;\n        mTopBarTextBtnTypeface = array.getBoolean(R.styleable.QMUITopBar_qmui_topbar_text_btn_bold, false) ? Typeface.DEFAULT_BOLD : null;\n        int ellipsize = array.getInt(R.styleable.QMUITopBar_android_ellipsize, -1) ;\n        switch (ellipsize) {\n            case 1:\n                mEllipsize = TextUtils.TruncateAt.START;\n                break;\n            case 2:\n                mEllipsize = TextUtils.TruncateAt.MIDDLE;\n                break;\n            case 3:\n                mEllipsize = TextUtils.TruncateAt.END;\n                break;\n            default:\n                mEllipsize = null;\n                break;\n        }\n        array.recycle();\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        ViewParent parent = getParent();\n        while (parent instanceof View) {\n            if (parent instanceof QMUICollapsingTopBarLayout) {\n                makeSureTitleContainerView();\n                return;\n            }\n            parent = parent.getParent();\n        }\n    }\n\n    /**\n     * 在 TopBar 的中间添加 View，如果此前已经有 View 通过该方法添加到 TopBar，则旧的View会被 remove\n     *\n     * @param view 要添加到TopBar中间的View\n     */\n    public void setCenterView(View view) {\n        if (mCenterView == view) {\n            return;\n        }\n        if (mCenterView != null) {\n            removeView(mCenterView);\n        }\n        mCenterView = view;\n        LayoutParams params = (LayoutParams) mCenterView.getLayoutParams();\n        if (params == null) {\n            params = new LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        }\n        params.addRule(RelativeLayout.CENTER_IN_PARENT);\n        addView(view, params);\n    }\n\n    /**\n     * 添加 TopBar 的标题\n     *\n     * @param resId TopBar 的标题 resId\n     */\n    public QMUIQQFaceView setTitle(int resId) {\n        return setTitle(getContext().getString(resId));\n    }\n\n    /**\n     * 添加 TopBar 的标题\n     *\n     * @param title TopBar 的标题\n     */\n    public QMUIQQFaceView setTitle(String title) {\n        QMUIQQFaceView titleView = ensureTitleView();\n        titleView.setText(title);\n        if (QMUILangHelper.isNullOrEmpty(title)) {\n            titleView.setVisibility(GONE);\n        } else {\n            titleView.setVisibility(VISIBLE);\n        }\n        return titleView;\n    }\n\n    public CharSequence getTitle() {\n        if (mTitleView == null) {\n            return null;\n        }\n        return mTitleView.getText();\n    }\n\n    @Nullable\n    public QMUIQQFaceView getTitleView(){\n        return mTitleView;\n    }\n\n    public void showTitleView(boolean toShow) {\n        if (mTitleView != null) {\n            mTitleView.setVisibility(toShow ? VISIBLE : GONE);\n        }\n    }\n\n    private QMUIQQFaceView ensureTitleView() {\n        if (mTitleView == null) {\n            mTitleView = new QMUIQQFaceView(getContext());\n            mTitleView.setGravity(Gravity.CENTER);\n            mTitleView.setSingleLine(true);\n            mTitleView.setEllipsize(mEllipsize);\n            mTitleView.setTypeface(mTitleTypeface);\n            mTitleView.setTextColor(mTitleTextColor);\n            QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n            provider.setDefaultSkinAttr(QMUISkinValueBuilder.TEXT_COLOR, R.attr.qmui_skin_support_topbar_title_color);\n            mTitleView.setTag(R.id.qmui_skin_default_attr_provider, provider);\n            updateTitleViewStyle();\n            LinearLayout.LayoutParams titleLp = generateTitleViewAndSubTitleViewLp();\n            makeSureTitleContainerView().addView(mTitleView, titleLp);\n        }\n\n        return mTitleView;\n    }\n\n    /**\n     * 更新 titleView 的样式（因为有没有 subTitle 会影响 titleView 的样式）\n     */\n    private void updateTitleViewStyle() {\n        if (mTitleView != null) {\n            if (mSubTitleView == null || QMUILangHelper.isNullOrEmpty(mSubTitleView.getText())) {\n                mTitleView.setTextSize(mTitleTextSize);\n            } else {\n                mTitleView.setTextSize(mTitleTextSizeWithSubTitle);\n            }\n        }\n    }\n\n    /**\n     * 添加 TopBar 的副标题\n     *\n     * @param subTitle TopBar 的副标题\n     */\n    public QMUISpanTouchFixTextView setSubTitle(CharSequence subTitle) {\n        QMUISpanTouchFixTextView subTitleView = ensureSubTitleView();\n        subTitleView.setText(subTitle);\n        if (QMUILangHelper.isNullOrEmpty(subTitle)) {\n            subTitleView.setVisibility(GONE);\n        } else {\n            subTitleView.setVisibility(VISIBLE);\n        }\n        // 更新 titleView 的样式（因为有没有 subTitle 会影响 titleView 的样式）\n        updateTitleViewStyle();\n        return subTitleView;\n    }\n\n    /**\n     * 添加 TopBar 的副标题\n     *\n     * @param resId TopBar 的副标题 resId\n     */\n    public QMUISpanTouchFixTextView setSubTitle(int resId) {\n        return setSubTitle(getResources().getString(resId));\n    }\n\n    private QMUISpanTouchFixTextView ensureSubTitleView() {\n        if (mSubTitleView == null) {\n            mSubTitleView = new QMUISpanTouchFixTextView(getContext());\n            mSubTitleView.setGravity(Gravity.CENTER);\n            mSubTitleView.setSingleLine(true);\n            mSubTitleView.setTypeface(mSubTitleTypeface);\n            mSubTitleView.setEllipsize(mEllipsize);\n            mSubTitleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSubTitleTextSize);\n            mSubTitleView.setTextColor(mSubTitleTextColor);\n            QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n            provider.setDefaultSkinAttr(QMUISkinValueBuilder.TEXT_COLOR, R.attr.qmui_skin_support_topbar_subtitle_color);\n            mSubTitleView.setTag(R.id.qmui_skin_default_attr_provider, provider);\n            LinearLayout.LayoutParams titleLp = generateTitleViewAndSubTitleViewLp();\n            titleLp.topMargin = QMUIDisplayHelper.dp2px(getContext(), 1);\n            makeSureTitleContainerView().addView(mSubTitleView, titleLp);\n        }\n\n        return mSubTitleView;\n    }\n\n    @Nullable\n    public QMUISpanTouchFixTextView getSubTitleView(){\n        return mSubTitleView;\n    }\n\n    /**\n     * 设置 TopBar 的 gravity，用于控制 title 和 subtitle 的对齐方式\n     *\n     * @param gravity 参考 {@link android.view.Gravity}\n     */\n    public void setTitleGravity(int gravity) {\n        mTitleGravity = gravity;\n        if (mTitleView != null) {\n            ((LinearLayout.LayoutParams) mTitleView.getLayoutParams()).gravity = gravity;\n            if (gravity == Gravity.CENTER || gravity == Gravity.CENTER_HORIZONTAL) {\n                mTitleView.setPadding(getPaddingLeft(), getPaddingTop(), getPaddingLeft(), getPaddingBottom());\n            }\n        }\n        if (mSubTitleView != null) {\n            ((LinearLayout.LayoutParams) mSubTitleView.getLayoutParams()).gravity = gravity;\n        }\n        requestLayout();\n    }\n\n    public Rect getTitleContainerRect() {\n        if (mTitleContainerRect == null) {\n            mTitleContainerRect = new Rect();\n        }\n        if (mTitleContainerView == null) {\n            mTitleContainerRect.set(0, 0, 0, 0);\n        } else {\n            QMUIViewHelper.getDescendantRect(this, mTitleContainerView, mTitleContainerRect);\n        }\n        return mTitleContainerRect;\n    }\n\n    public LinearLayout getTitleContainerView() {\n        return mTitleContainerView;\n    }\n\n    void disableBackgroundSetter(){\n        mIsBackgroundSetterDisabled = true;\n        super.setBackgroundDrawable(null);\n    }\n\n    @Override\n    public void setBackgroundDrawable(Drawable background) {\n        if(!mIsBackgroundSetterDisabled){\n            super.setBackgroundDrawable(background);\n        }\n    }\n\n    // ========================= leftView、rightView 相关的方法\n\n    private LinearLayout makeSureTitleContainerView() {\n        if (mTitleContainerView == null) {\n            mTitleContainerView = new LinearLayout(getContext());\n            // 垂直，后面要支持水平的话可以加个接口来设置\n            mTitleContainerView.setOrientation(LinearLayout.VERTICAL);\n            mTitleContainerView.setGravity(Gravity.CENTER);\n            mTitleContainerView.setPadding(mTitleContainerPaddingHor, 0, mTitleContainerPaddingHor, 0);\n            addView(mTitleContainerView, generateTitleContainerViewLp());\n        }\n        return mTitleContainerView;\n    }\n\n    /**\n     * 生成 TitleContainerView 的 LayoutParams。\n     * 左右有按钮时，该 View 在左右按钮之间；\n     * 没有左右按钮时，该 View 距离 TopBar 左右边缘有固定的距离\n     */\n    private LayoutParams generateTitleContainerViewLp() {\n        return new LayoutParams(LayoutParams.MATCH_PARENT,\n                QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_topbar_height));\n    }\n\n    /**\n     * 生成 titleView 或 subTitleView 的 LayoutParams\n     */\n    private LinearLayout.LayoutParams generateTitleViewAndSubTitleViewLp() {\n        LinearLayout.LayoutParams titleLp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\n        // 垂直居中\n        titleLp.gravity = mTitleGravity;\n        return titleLp;\n    }\n\n    /**\n     * 在TopBar的左侧添加View，如果此前已经有View通过该方法添加到TopBar，则新添加进去的View会出现在已有View的右侧\n     *\n     * @param view   要添加到 TopBar 左边的 View\n     * @param viewId 该按钮的id，可在ids.xml中找到合适的或新增。手工指定viewId是为了适应自动化测试。\n     */\n    public void addLeftView(View view, int viewId) {\n        ViewGroup.LayoutParams viewLayoutParams = view.getLayoutParams();\n        LayoutParams layoutParams;\n        if (viewLayoutParams != null && viewLayoutParams instanceof LayoutParams) {\n            layoutParams = (LayoutParams) viewLayoutParams;\n        } else {\n            layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\n        }\n        this.addLeftView(view, viewId, layoutParams);\n    }\n\n    /**\n     * 在TopBar的左侧添加View，如果此前已经有View通过该方法添加到TopBar，则新添加进去的View会出现在已有View的右侧。\n     *\n     * @param view         要添加到 TopBar 左边的 View。\n     * @param viewId       该按钮的 id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @param layoutParams 传入一个 LayoutParams，当把 Button addView 到 TopBar 时，使用这个 LayouyParams。\n     */\n    public void addLeftView(View view, int viewId, LayoutParams layoutParams) {\n        if (mLeftLastViewId == DEFAULT_VIEW_ID) {\n            layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);\n        } else {\n            layoutParams.addRule(RelativeLayout.RIGHT_OF, mLeftLastViewId);\n        }\n        layoutParams.alignWithParent = true; // alignParentIfMissing\n        mLeftLastViewId = viewId;\n        view.setId(viewId);\n        mLeftViewList.add(view);\n        addView(view, layoutParams);\n    }\n\n    /**\n     * 在 TopBar 的右侧添加 View，如果此前已经有 iew 通过该方法添加到 TopBar，则新添加进去的View会出现在已有View的左侧\n     *\n     * @param view   要添加到 TopBar 右边的View\n     * @param viewId 该按钮的id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     */\n    public void addRightView(View view, int viewId) {\n        ViewGroup.LayoutParams viewLayoutParams = view.getLayoutParams();\n        LayoutParams layoutParams;\n        if (viewLayoutParams != null && viewLayoutParams instanceof LayoutParams) {\n            layoutParams = (LayoutParams) viewLayoutParams;\n        } else {\n            layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\n        }\n        this.addRightView(view, viewId, layoutParams);\n    }\n\n    /**\n     * 在 TopBar 的右侧添加 View，如果此前已经有 View 通过该方法添加到 TopBar，则新添加进去的 View 会出现在已有View的左侧。\n     *\n     * @param view         要添加到 TopBar 右边的 View。\n     * @param viewId       该按钮的 id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @param layoutParams 生成一个 LayoutParams，当把 Button addView 到 TopBar 时，使用这个 LayouyParams。\n     */\n    public void addRightView(View view, int viewId, LayoutParams layoutParams) {\n        if (mRightLastViewId == DEFAULT_VIEW_ID) {\n            layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);\n        } else {\n            layoutParams.addRule(RelativeLayout.LEFT_OF, mRightLastViewId);\n        }\n        layoutParams.alignWithParent = true; // alignParentIfMissing\n        mRightLastViewId = viewId;\n        view.setId(viewId);\n        mRightViewList.add(view);\n        addView(view, layoutParams);\n    }\n\n    public LayoutParams generateTopBarImageButtonLayoutParams(){\n        return generateTopBarImageButtonLayoutParams(-1, -1);\n    }\n\n    /**\n     * 生成一个 LayoutParams，当把 Button addView 到 TopBar 时，使用这个 LayouyParams\n     */\n    public LayoutParams generateTopBarImageButtonLayoutParams(int iconWidth, int iconHeight) {\n        iconHeight = iconHeight > 0 ? iconHeight : mTopBarImageBtnHeight;\n        LayoutParams lp = new LayoutParams(iconWidth > 0 ? iconWidth : mTopBarImageBtnWidth, iconHeight);\n        lp.topMargin = Math.max(0, (getTopBarHeight() - iconHeight) / 2);\n        return lp;\n    }\n\n\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, int viewId) {\n        return addRightImageButton(drawableResId, true, viewId);\n    }\n\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, boolean followTintColor, int viewId) {\n        return addRightImageButton(drawableResId, followTintColor, viewId, -1, -1);\n    }\n\n    /**\n     * 根据 resourceId 生成一个 TopBar 的按钮，并 add 到 TopBar 的右侧\n     *\n     * @param drawableResId   按钮图片的 resourceId\n     * @param viewId          该按钮的 id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @param followTintColor 换肤时使用 tintColor 更改它的颜色\n     * @return 返回生成的按钮\n     */\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, boolean followTintColor, int viewId, int iconWidth, int iconHeight) {\n        QMUIAlphaImageButton rightButton = generateTopBarImageButton(drawableResId, followTintColor);\n        this.addRightView(rightButton, viewId, generateTopBarImageButtonLayoutParams(iconWidth, iconHeight));\n        return rightButton;\n    }\n\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, int viewId) {\n        return addLeftImageButton(drawableResId, true, viewId);\n    }\n\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, boolean followTintColor, int viewId) {\n        return addLeftImageButton(drawableResId, followTintColor, viewId, -1, -1);\n    }\n\n    /**\n     * 根据 resourceId 生成一个 TopBar 的按钮，并 add 到 TopBar 的左边\n     *\n     * @param drawableResId   按钮图片的 resourceId\n     * @param viewId          该按钮的 id，可在ids.xml中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @param followTintColor 换肤时使用 tintColor 更改它的颜色\n     * @return 返回生成的按钮\n     */\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, boolean followTintColor, int viewId, int iconWidth, int iconHeight) {\n        QMUIAlphaImageButton leftButton = generateTopBarImageButton(drawableResId, followTintColor);\n        this.addLeftView(leftButton, viewId, generateTopBarImageButtonLayoutParams(iconWidth, iconHeight));\n        return leftButton;\n    }\n\n    /**\n     * 生成一个LayoutParams，当把 Button addView 到 TopBar 时，使用这个 LayouyParams\n     */\n    public LayoutParams generateTopBarTextButtonLayoutParams() {\n        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, mTopBarImageBtnHeight);\n        lp.topMargin = Math.max(0, (getTopBarHeight() - mTopBarImageBtnHeight) / 2);\n        return lp;\n    }\n\n    /**\n     * 在 TopBar 左边添加一个 Button，并设置文字\n     *\n     * @param stringResId 按钮的文字的 resourceId\n     * @param viewId      该按钮的id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @return 返回生成的按钮\n     */\n    public Button addLeftTextButton(int stringResId, int viewId) {\n        return addLeftTextButton(getResources().getString(stringResId), viewId);\n    }\n\n    /**\n     * 在 TopBar 左边添加一个 Button，并设置文字\n     *\n     * @param buttonText 按钮的文字\n     * @param viewId     该按钮的 id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @return 返回生成的按钮\n     */\n    public Button addLeftTextButton(String buttonText, int viewId) {\n        Button button = generateTopBarTextButton(buttonText);\n        this.addLeftView(button, viewId, generateTopBarTextButtonLayoutParams());\n        return button;\n    }\n\n    /**\n     * 在 TopBar 右边添加一个 Button，并设置文字\n     *\n     * @param stringResId 按钮的文字的 resourceId\n     * @param viewId      该按钮的id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @return 返回生成的按钮\n     */\n    public Button addRightTextButton(int stringResId, int viewId) {\n        return addRightTextButton(getResources().getString(stringResId), viewId);\n    }\n\n    /**\n     * 在 TopBar 右边添加一个 Button，并设置文字\n     *\n     * @param buttonText 按钮的文字\n     * @param viewId     该按钮的 id，可在 ids.xml 中找到合适的或新增。手工指定 viewId 是为了适应自动化测试。\n     * @return 返回生成的按钮\n     */\n    public Button addRightTextButton(String buttonText, int viewId) {\n        Button button = generateTopBarTextButton(buttonText);\n        this.addRightView(button, viewId, generateTopBarTextButtonLayoutParams());\n        return button;\n    }\n\n\n    private IQMUISkinDefaultAttrProvider mTopBarTextDefaultAttrProvider;\n\n    /**\n     * 生成一个文本按钮，并设置文字\n     *\n     * @param text 按钮的文字\n     * @return 返回生成的按钮\n     */\n    private Button generateTopBarTextButton(String text) {\n        Button button = new Button(getContext());\n        if (mTopBarTextDefaultAttrProvider == null) {\n            QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n            provider.setDefaultSkinAttr(\n                    QMUISkinValueBuilder.TEXT_COLOR, R.attr.qmui_skin_support_topbar_text_btn_color_state_list);\n            mTopBarTextDefaultAttrProvider = provider;\n\n        }\n        button.setTag(R.id.qmui_skin_default_attr_provider, mTopBarTextDefaultAttrProvider);\n        button.setBackgroundResource(0);\n        button.setMinWidth(0);\n        button.setMinHeight(0);\n        button.setMinimumWidth(0);\n        button.setMinimumHeight(0);\n        button.setTypeface(mTopBarTextBtnTypeface);\n        button.setPadding(mTopBarTextBtnPaddingHor, 0, mTopBarTextBtnPaddingHor, 0);\n        button.setTextColor(mTopBarTextBtnTextColor);\n        button.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTopBarTextBtnTextSize);\n        button.setGravity(Gravity.CENTER);\n        button.setText(text);\n        return button;\n    }\n\n\n    private IQMUISkinDefaultAttrProvider mTopBarImageColorTintColorProvider;\n\n    /**\n     * 生成一个图片按钮，配合 {{@link #generateTopBarImageButtonLayoutParams()} 使用\n     *\n     * @param imageResourceId 图片的 resId\n     */\n    private QMUIAlphaImageButton generateTopBarImageButton(int imageResourceId, boolean followTintColor) {\n        QMUIAlphaImageButton imageButton = new QMUIAlphaImageButton(getContext());\n        if (followTintColor) {\n            if (mTopBarImageColorTintColorProvider == null) {\n                QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n                provider.setDefaultSkinAttr(\n                        QMUISkinValueBuilder.TINT_COLOR, R.attr.qmui_skin_support_topbar_image_tint_color);\n                mTopBarImageColorTintColorProvider = provider;\n            }\n            imageButton.setTag(R.id.qmui_skin_default_attr_provider, mTopBarImageColorTintColorProvider);\n        }\n        imageButton.setBackgroundColor(Color.TRANSPARENT);\n        imageButton.setImageResource(imageResourceId);\n        return imageButton;\n    }\n\n    /**\n     * 便捷方法，在 TopBar 左边添加一个返回图标按钮\n     *\n     * @return 返回按钮\n     */\n    public QMUIAlphaImageButton addLeftBackImageButton() {\n        if(mClearLeftPaddingWhenAddLeftBackView){\n            QMUIViewHelper.setPaddingLeft(this, 0);\n        }\n        if(mLeftBackViewWidth > 0){\n            return addLeftImageButton(mLeftBackDrawableRes, true, R.id.qmui_topbar_item_left_back, mLeftBackViewWidth, -1);\n        }\n        return addLeftImageButton(mLeftBackDrawableRes, R.id.qmui_topbar_item_left_back);\n    }\n\n    /**\n     * 移除 TopBar 左边所有的 View\n     */\n    public void removeAllLeftViews() {\n        for (View leftView : mLeftViewList) {\n            removeView(leftView);\n        }\n        mLeftLastViewId = DEFAULT_VIEW_ID;\n        mLeftViewList.clear();\n    }\n\n    /**\n     * 移除 TopBar 右边所有的 View\n     */\n    public void removeAllRightViews() {\n        for (View rightView : mRightViewList) {\n            removeView(rightView);\n        }\n        mRightLastViewId = DEFAULT_VIEW_ID;\n        mRightViewList.clear();\n    }\n\n    /**\n     * 移除 TopBar 的 centerView 和 titleView\n     */\n    public void removeCenterViewAndTitleView() {\n        if (mCenterView != null) {\n            if (mCenterView.getParent() == this) {\n                removeView(mCenterView);\n            }\n            mCenterView = null;\n        }\n\n        if (mTitleView != null) {\n            if (mTitleView.getParent() == this) {\n                removeView(mTitleView);\n            }\n            mTitleView = null;\n        }\n    }\n\n    int getTopBarHeight() {\n        if (mTopBarHeight == -1) {\n            mTopBarHeight = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_topbar_height);\n        }\n        return mTopBarHeight;\n    }\n\n    /**\n     * 设置 TopBar 背景的透明度\n     *\n     * @param alpha 取值范围：[0, 255]，255表示不透明\n     */\n    public void setBackgroundAlpha(int alpha) {\n        this.getBackground().mutate().setAlpha(alpha);\n    }\n\n    /**\n     * 根据当前 offset、透明度变化的初始 offset 和目标 offset，计算并设置 Topbar 的透明度\n     *\n     * @param currentOffset     当前 offset\n     * @param alphaBeginOffset  透明度开始变化的offset，即当 currentOffset == alphaBeginOffset 时，透明度为0\n     * @param alphaTargetOffset 透明度变化的目标offset，即当 currentOffset == alphaTargetOffset 时，透明度为1\n     */\n    public int computeAndSetBackgroundAlpha(int currentOffset, int alphaBeginOffset, int alphaTargetOffset) {\n        double alpha = (float) (currentOffset - alphaBeginOffset) / (alphaTargetOffset - alphaBeginOffset);\n        alpha = Math.max(0, Math.min(alpha, 1)); // from 0 to 1\n        int alphaInt = (int) (alpha * 255);\n        setBackgroundAlpha(alphaInt);\n        return alphaInt;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        if (mTitleContainerView != null) {\n            // 计算左侧 View 的总宽度\n            int leftViewWidth = getPaddingLeft();\n            for (int leftViewIndex = 0; leftViewIndex < mLeftViewList.size(); leftViewIndex++) {\n                View view = mLeftViewList.get(leftViewIndex);\n                if (view.getVisibility() != GONE) {\n                    leftViewWidth += view.getMeasuredWidth();\n                }\n            }\n            // 计算右侧 View 的总宽度\n            int rightViewWidth = getPaddingRight();\n            for (int rightViewIndex = 0; rightViewIndex < mRightViewList.size(); rightViewIndex++) {\n                View view = mRightViewList.get(rightViewIndex);\n                if (view.getVisibility() != GONE) {\n                    rightViewWidth += view.getMeasuredWidth();\n                }\n            }\n\n            leftViewWidth = Math.max(mTitleMarginHorWhenNoBtnAside, leftViewWidth);\n            rightViewWidth = Math.max(mTitleMarginHorWhenNoBtnAside, rightViewWidth);\n\n            // 计算 titleContainer 的最大宽度\n            int titleContainerWidth;\n            if ((mTitleGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {\n\n\n                // 标题水平居中，左右两侧的占位要保持一致\n                titleContainerWidth = MeasureSpec.getSize(widthMeasureSpec) -\n                        Math.max(leftViewWidth, rightViewWidth) * 2;\n            } else {\n                // 标题非水平居中，左右两侧的占位按实际计算即可\n                titleContainerWidth = MeasureSpec.getSize(widthMeasureSpec) - leftViewWidth - rightViewWidth;\n            }\n            int titleContainerWidthMeasureSpec = MeasureSpec.makeMeasureSpec(titleContainerWidth, MeasureSpec.EXACTLY);\n            mTitleContainerView.measure(titleContainerWidthMeasureSpec, heightMeasureSpec);\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        super.onLayout(changed, l, t, r, b);\n        if (mTitleContainerView != null) {\n            int titleContainerViewWidth = mTitleContainerView.getMeasuredWidth();\n            int titleContainerViewHeight = mTitleContainerView.getMeasuredHeight();\n            int titleContainerViewTop = (b - t - mTitleContainerView.getMeasuredHeight()) / 2;\n            int titleContainerViewLeft = getPaddingLeft();\n            if ((mTitleGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {\n                // 标题水平居中\n                titleContainerViewLeft = (r - l - mTitleContainerView.getMeasuredWidth()) / 2;\n            } else {\n                // 标题非水平居中\n                // 计算左侧 View 的总宽度\n                for (int leftViewIndex = 0; leftViewIndex < mLeftViewList.size(); leftViewIndex++) {\n                    View view = mLeftViewList.get(leftViewIndex);\n                    if (view.getVisibility() != GONE) {\n                        titleContainerViewLeft += view.getMeasuredWidth();\n                    }\n                }\n\n                titleContainerViewLeft = Math.max(titleContainerViewLeft, mTitleMarginHorWhenNoBtnAside);\n            }\n            mTitleContainerView.layout(titleContainerViewLeft, titleContainerViewTop,\n                    titleContainerViewLeft + titleContainerViewWidth,\n                    titleContainerViewTop + titleContainerViewHeight);\n        }\n    }\n\n    @Override\n    public void handle(@NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme, @Nullable SimpleArrayMap<String, Integer> attrs) {\n        if (attrs != null) {\n            for (int i = 0; i < attrs.size(); i++) {\n                String key = attrs.keyAt(i);\n                Integer attr = attrs.valueAt(i);\n                if (attr == null) {\n                    continue;\n                }\n                if (getParent() instanceof QMUITopBarLayout &&\n                        (QMUISkinValueBuilder.BACKGROUND.equals(key) ||\n                                QMUISkinValueBuilder.BOTTOM_SEPARATOR.equals(key))) {\n                    // handled by parent\n                    continue;\n                }\n                manager.defaultHandleSkinAttr(this, theme, key, attr);\n            }\n        }\n    }\n\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n\n    public void eachLeftRightView(@NonNull Action action){\n        for(int i = 0; i < mLeftViewList.size(); i++){\n            action.call(mLeftViewList.get(i), i, true);\n        }\n        for(int i = 0; i < mRightViewList.size(); i++){\n            action.call(mRightViewList.get(i), i, false);\n        }\n    }\n\n    public interface Action {\n        void call(View view, int index, boolean isLeftView);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUITopBarLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.FrameLayout;\nimport android.widget.RelativeLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaImageButton;\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\n/**\n * 这是一个对 {@link QMUITopBar} 的代理类，需要它的原因是：\n * 我们用 fitSystemWindows 实现沉浸式状态栏后，需要将 {@link QMUITopBar} 的背景衍生到状态栏后面，这个时候 fitSystemWindows 是通过\n * 更改 padding 实现的，而 {@link QMUITopBar} 是在高度固定的前提下做各种行为的，例如按钮的垂直居中，因此我们需要在外面包裹一层并消耗 padding\n *\n * @author cginechen\n * @date 2016-11-26\n */\n\npublic class QMUITopBarLayout extends QMUIFrameLayout implements IQMUISkinDefaultAttrProvider {\n    private QMUITopBar mTopBar;\n    private SimpleArrayMap<String, Integer> mDefaultSkinAttrs = new SimpleArrayMap<>(2);\n\n    public QMUITopBarLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUITopBarLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUITopBarStyle);\n    }\n\n    public QMUITopBarLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        mDefaultSkinAttrs.put(QMUISkinValueBuilder.BOTTOM_SEPARATOR, R.attr.qmui_skin_support_topbar_separator_color);\n        mDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_topbar_bg);\n        mTopBar = new QMUITopBar(context, attrs, defStyleAttr);\n        mTopBar.setBackground(null);\n        mTopBar.setVisibility(View.VISIBLE);\n        // reset these field because mTopBar will set same value with QMUITopBarLayout from attrs\n        mTopBar.setFitsSystemWindows(false);\n        mTopBar.setId(View.generateViewId());\n        mTopBar.updateBottomDivider(0, 0, 0, 0);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, mTopBar.getTopBarHeight());\n        addView(mTopBar, lp);\n        QMUIWindowInsetHelper.handleWindowInsets(this,\n                WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout(), true, true);\n    }\n\n    public QMUITopBar getTopBar() {\n        return mTopBar;\n    }\n\n    public void setCenterView(View view) {\n        mTopBar.setCenterView(view);\n    }\n\n    public QMUIQQFaceView setTitle(int resId) {\n        return mTopBar.setTitle(resId);\n    }\n\n    public QMUIQQFaceView setTitle(String title) {\n        return mTopBar.setTitle(title);\n    }\n\n    public void showTitleView(boolean toShow) {\n        mTopBar.showTitleView(toShow);\n    }\n\n    public QMUISpanTouchFixTextView setSubTitle(int resId) {\n        return mTopBar.setSubTitle(resId);\n    }\n\n    public QMUISpanTouchFixTextView setSubTitle(CharSequence subTitle) {\n        return mTopBar.setSubTitle(subTitle);\n    }\n\n    @Nullable\n    public QMUIQQFaceView getTitleView(){\n        return mTopBar.getTitleView();\n    }\n\n    @Nullable\n    public QMUISpanTouchFixTextView getSubTitleView(){\n        return mTopBar.getSubTitleView();\n    }\n\n    public void setTitleGravity(int gravity) {\n        mTopBar.setTitleGravity(gravity);\n    }\n\n    public void addLeftView(View view, int viewId) {\n        mTopBar.addLeftView(view, viewId);\n    }\n\n    public void addLeftView(View view, int viewId, RelativeLayout.LayoutParams layoutParams) {\n        mTopBar.addLeftView(view, viewId, layoutParams);\n    }\n\n    public void addRightView(View view, int viewId) {\n        mTopBar.addRightView(view, viewId);\n    }\n\n    public void addRightView(View view, int viewId, RelativeLayout.LayoutParams layoutParams) {\n        mTopBar.addRightView(view, viewId, layoutParams);\n    }\n\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, int viewId) {\n        return mTopBar.addRightImageButton(drawableResId, viewId);\n    }\n\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, boolean followTintColor, int viewId) {\n        return mTopBar.addRightImageButton(drawableResId, followTintColor, viewId);\n    }\n\n    public QMUIAlphaImageButton addRightImageButton(int drawableResId, boolean followTintColor, int viewId, int iconWidth, int iconHeight) {\n        return mTopBar.addRightImageButton(drawableResId, followTintColor, viewId, iconWidth, iconHeight);\n    }\n\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, int viewId) {\n        return mTopBar.addLeftImageButton(drawableResId, viewId);\n    }\n\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, boolean followTintColor, int viewId) {\n        return mTopBar.addLeftImageButton(drawableResId, followTintColor, viewId);\n    }\n\n    public QMUIAlphaImageButton addLeftImageButton(int drawableResId, boolean followTintColor, int viewId, int iconWidth, int iconHeight) {\n        return mTopBar.addLeftImageButton(drawableResId, followTintColor, viewId, iconWidth, iconHeight);\n    }\n\n    public Button addLeftTextButton(int stringResId, int viewId) {\n        return mTopBar.addLeftTextButton(stringResId, viewId);\n    }\n\n    public Button addLeftTextButton(String buttonText, int viewId) {\n        return mTopBar.addLeftTextButton(buttonText, viewId);\n    }\n\n    public Button addRightTextButton(int stringResId, int viewId) {\n        return mTopBar.addRightTextButton(stringResId, viewId);\n    }\n\n    public Button addRightTextButton(String buttonText, int viewId) {\n        return mTopBar.addRightTextButton(buttonText, viewId);\n    }\n\n    public QMUIAlphaImageButton addLeftBackImageButton() {\n        return mTopBar.addLeftBackImageButton();\n    }\n\n    public void removeAllLeftViews() {\n        mTopBar.removeAllLeftViews();\n    }\n\n    public void removeAllRightViews() {\n        mTopBar.removeAllRightViews();\n    }\n\n    public void removeCenterViewAndTitleView() {\n        mTopBar.removeCenterViewAndTitleView();\n    }\n\n    /**\n     * 设置 TopBar 背景的透明度\n     *\n     * @param alpha 取值范围：[0, 255]，255表示不透明\n     */\n    public void setBackgroundAlpha(int alpha) {\n        this.getBackground().mutate().setAlpha(alpha);\n    }\n\n    /**\n     * 根据当前 offset、透明度变化的初始 offset 和目标 offset，计算并设置 Topbar 的透明度\n     *\n     * @param currentOffset     当前 offset\n     * @param alphaBeginOffset  透明度开始变化的offset，即当 currentOffset == alphaBeginOffset 时，透明度为0\n     * @param alphaTargetOffset 透明度变化的目标offset，即当 currentOffset == alphaTargetOffset 时，透明度为1\n     */\n    public int computeAndSetBackgroundAlpha(int currentOffset, int alphaBeginOffset, int alphaTargetOffset) {\n        double alpha = (float) (currentOffset - alphaBeginOffset) / (alphaTargetOffset - alphaBeginOffset);\n        alpha = Math.max(0, Math.min(alpha, 1)); // from 0 to 1\n        int alphaInt = (int) (alpha * 255);\n        this.setBackgroundAlpha(alphaInt);\n        return alphaInt;\n    }\n\n    public void setDefaultSkinAttr(String name, int attr) {\n        mDefaultSkinAttrs.put(name, attr);\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return mDefaultSkinAttrs;\n    }\n\n    public void eachLeftRightView(@NonNull QMUITopBar.Action action){\n        mTopBar.eachLeftRightView(action);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIVerticalTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.os.Build;\nimport android.text.TextPaint;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\nimport androidx.appcompat.widget.AppCompatTextView;\n\n/**\n * 在 {@link TextView} 的基础上支持文字竖排\n *\n * <p>默认将文字竖排显示, 可使用 {@link #setVerticalMode(boolean)} 来开启/关闭竖排功能</p>\n */\npublic class QMUIVerticalTextView extends AppCompatTextView {\n\n    /**\n     * 是否将文字显示成竖排\n     */\n    private boolean mIsVerticalMode = true;\n\n    private int mLineCount; // 行数\n    private float[] mLineWidths; // 下标: 行号; 数组内容: 该行的宽度(由该行最宽的字符决定)\n    private int[] mLineBreakIndex; // 下标: 行号; 数组内容: 该行最后一个字符的下标\n\n    public QMUIVerticalTextView(Context context) {\n        super(context);\n        init();\n    }\n\n    public QMUIVerticalTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public QMUIVerticalTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n    }\n\n    @SuppressLint(\"DrawAllocation\")\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        if (mIsVerticalMode) {\n            int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n            int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n            int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n            int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\n            float width = getPaddingLeft() + getPaddingRight();\n            float height = getPaddingTop() + getPaddingBottom();\n            char[] chars = getText().toString().toCharArray();\n            final Paint paint = getPaint();\n            final Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();\n\n            final int lineMaxBottom = (heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : heightSize)\n                    - getPaddingBottom();\n\n            float currentLineHeight = getPaddingTop();\n            float lineMaxHeight = currentLineHeight;\n            int lineIndex = 0;\n            mLineCount = 0;\n            mLineWidths = new float[chars.length + 1]; // 加1是为了处理高度不够放下一个字的情况,needBreakLine会一直为true直到最后一个字\n            mLineBreakIndex = new int[chars.length + 1];\n            // 从右向左,从上向下布局\n            int step = 1;\n            for (int i = 0; i < chars.length; i += step) {\n                int codePoint = Character.codePointAt(chars, i);\n                step = Character.charCount(codePoint);\n                // rotate\n                boolean needRotate = !isCJKCharacter(codePoint);\n                // char height\n                float charHeight;\n                float charWidth;\n                if (needRotate) {\n                    charWidth = fontMetricsInt.descent - fontMetricsInt.ascent;\n                    charHeight = paint.measureText(chars, i, step);\n                } else {\n                    charWidth = paint.measureText(chars, i, step);\n                    charHeight = fontMetricsInt.descent - fontMetricsInt.ascent;\n                }\n\n                // is need break line\n                boolean needBreakLine = currentLineHeight + charHeight > lineMaxBottom\n                        && i > 0; // i > 0 是因为即使在第一列高度不够,也不用换下一列\n                if (needBreakLine) {\n                    // break line\n                    if (lineMaxHeight < currentLineHeight) {\n                        lineMaxHeight = currentLineHeight;\n                    }\n                    mLineBreakIndex[lineIndex] = i - step;\n                    width += mLineWidths[lineIndex];\n                    lineIndex++;\n                    // reset\n                    currentLineHeight = getPaddingTop() + charHeight;\n                } else {\n                    currentLineHeight += charHeight;\n                    if (lineMaxHeight < currentLineHeight) {\n                        lineMaxHeight = currentLineHeight;\n                    }\n                }\n\n                if (mLineWidths[lineIndex] < charWidth) {\n                    mLineWidths[lineIndex] = charWidth;\n                }\n                // last column width\n                if (i + step >= chars.length) {\n                    width += mLineWidths[lineIndex];\n                    height = lineMaxHeight + getPaddingBottom();\n                }\n            }\n            if (chars.length > 0) {\n                mLineCount = lineIndex + 1;\n                mLineBreakIndex[lineIndex] = chars.length - step;\n            }\n\n            // 计算 lineSpacing\n            if (mLineCount > 1) {\n                int lineSpacingCount = mLineCount - 1;\n                for (int i = 0; i < lineSpacingCount; i++) {\n                    width += mLineWidths[i] * (getLineSpacingMultiplier() - 1) + getLineSpacingExtra();\n                }\n            }\n\n            if (heightMode == MeasureSpec.EXACTLY) {\n                height = heightSize;\n            } else if (heightMode == MeasureSpec.AT_MOST) {\n                height = Math.min(height, heightSize);\n            }\n            if (widthMode == MeasureSpec.EXACTLY) {\n                width = widthSize;\n            } else if (widthMode == MeasureSpec.AT_MOST) {\n                width = Math.min(width, widthSize);\n            }\n\n            setMeasuredDimension((int) width, (int) height);\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    @Override\n    protected void onDraw(Canvas canvas) {\n        if (!mIsVerticalMode) {\n            super.onDraw(canvas);\n        } else {\n            if (mLineCount == 0) {\n                return;\n            }\n\n            final TextPaint paint = getPaint();\n            paint.setColor(getCurrentTextColor());\n            paint.drawableState = getDrawableState();\n            final Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();\n            final char[] chars = getText().toString().toCharArray();\n\n            canvas.save();\n\n            int curLine = 0;\n            float curLineX = getWidth() - getPaddingRight() - mLineWidths[curLine];\n            float curX = curLineX;\n            float curY = getPaddingTop();\n            int step;\n            for (int i = 0; i < chars.length; i+=step) {\n                int codePoint = Character.codePointAt(chars, i);\n                step = Character.charCount(codePoint);\n                boolean needRotate = !isCJKCharacter(codePoint);\n                final int saveCount = canvas.save();\n                if (needRotate) {\n                    canvas.rotate(90, curX, curY);\n                }\n                // draw\n                float textX = curX;\n                float textBaseline = needRotate ?\n                        curY - (mLineWidths[curLine] - (fontMetricsInt.bottom - fontMetricsInt.top)) / 2 - fontMetricsInt.descent :\n                        curY - fontMetricsInt.ascent;\n                canvas.drawText(chars, i, step, textX, textBaseline, paint);\n                canvas.restoreToCount(saveCount);\n\n                // if break line\n                boolean hasNextChar = i + step < chars.length;\n                if (hasNextChar) {\n//                boolean breakLine = needBreakLine(i, mLineCharsCount, curLine);\n                    boolean nextCharBreakLine = i + 1 > mLineBreakIndex[curLine];\n                    if (nextCharBreakLine && curLine + 1 < mLineWidths.length) {\n                        // new line\n                        curLine++;\n                        curLineX -= (mLineWidths[curLine] * getLineSpacingMultiplier() + getLineSpacingExtra());\n                        curX = curLineX;\n                        curY = getPaddingTop();\n                    } else {\n                        // move to next char\n                        if (needRotate) {\n                            curY += paint.measureText(chars, i, step);\n                        } else {\n                            curY += fontMetricsInt.descent - fontMetricsInt.ascent;\n                        }\n                    }\n                }\n            }\n\n            canvas.restore();\n        }\n    }\n\n    // This method is copied from moai.ik.helper.CharacterHelper.isCJKCharacter(char input)\n    private static boolean isCJKCharacter(int input) {\n        Character.UnicodeBlock ub = Character.UnicodeBlock.of(input);\n        //noinspection RedundantIfStatement\n        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS\n                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS\n                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A\n                //全角数字字符和日韩字符\n                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS\n                //韩文字符集\n                || ub == Character.UnicodeBlock.HANGUL_SYLLABLES\n                || ub == Character.UnicodeBlock.HANGUL_JAMO\n                || ub == Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO\n                //日文字符集\n                || ub == Character.UnicodeBlock.HIRAGANA //平假名\n                || ub == Character.UnicodeBlock.KATAKANA //片假名\n                || ub == Character.UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS\n                ) {\n            return true;\n        } else {\n            return false;\n        }\n        //其他的CJK标点符号，可以不做处理\n        //|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION\n        //|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION\n    }\n\n    public void setVerticalMode(boolean verticalMode) {\n        mIsVerticalMode = verticalMode;\n        requestLayout();\n    }\n\n    public boolean isVerticalMode() {\n        return mIsVerticalMode;\n    }\n\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIViewPager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.database.DataSetObserver;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.ViewCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\n\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\npublic class QMUIViewPager extends ViewPager {\n    private static final int DEFAULT_INFINITE_RATIO = 100;\n\n    private boolean mIsSwipeable = true;\n    private boolean mIsInMeasure = false;\n    private boolean mEnableLoop = false;\n    private int mInfiniteRatio = DEFAULT_INFINITE_RATIO;\n\n    public QMUIViewPager(Context context) {\n        this(context, null);\n    }\n\n    public QMUIViewPager(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        QMUIWindowInsetHelper.overrideWithDoNotHandleWindowInsets(this);\n    }\n\n    public void setSwipeable(boolean enable) {\n        mIsSwipeable = enable;\n    }\n\n    public int getInfiniteRatio() {\n        return mInfiniteRatio;\n    }\n\n    public void setInfiniteRatio(int infiniteRatio) {\n        mInfiniteRatio = infiniteRatio;\n    }\n\n    public boolean isEnableLoop() {\n        return mEnableLoop;\n    }\n\n    public void setEnableLoop(boolean enableLoop) {\n        if (mEnableLoop != enableLoop) {\n            mEnableLoop = enableLoop;\n            if (getAdapter() != null) {\n                getAdapter().notifyDataSetChanged();\n            }\n        }\n    }\n\n    @Override\n    public void onViewAdded(View child) {\n        super.onViewAdded(child);\n        ViewCompat.requestApplyInsets(child);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        try {\n            return mIsSwipeable && super.onTouchEvent(ev);\n        } catch (IllegalArgumentException ignore) {\n            return false;\n        }\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        try {\n            return  mIsSwipeable && super.onInterceptTouchEvent(ev);\n        } catch (IllegalArgumentException ignore) {\n            return false;\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        mIsInMeasure = true;\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        mIsInMeasure = false;\n    }\n\n    public boolean isInMeasure() {\n        return mIsInMeasure;\n    }\n\n    @Override\n    public void setAdapter(PagerAdapter adapter) {\n        if (adapter instanceof QMUIPagerAdapter) {\n            super.setAdapter(new WrapperPagerAdapter((QMUIPagerAdapter) adapter));\n        } else {\n            super.setAdapter(adapter);\n        }\n    }\n\n    class WrapperPagerAdapter extends PagerAdapter {\n        private QMUIPagerAdapter mAdapter;\n\n        public WrapperPagerAdapter(QMUIPagerAdapter adapter) {\n            mAdapter = adapter;\n        }\n\n        @Override\n        public int getCount() {\n            int count = mAdapter.getCount();\n            if (mEnableLoop && count > 3) {\n                count *= mInfiniteRatio;\n            }\n            return count;\n        }\n\n        @Override\n        @NonNull\n        public Object instantiateItem(@NonNull ViewGroup container, int position) {\n            int realPosition = position;\n            if (mEnableLoop && mAdapter.getCount() != 0) {\n                realPosition = position % mAdapter.getCount();\n            }\n            return mAdapter.instantiateItem(container, realPosition);\n        }\n\n        @Override\n        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {\n            int realPosition = position;\n            if (mEnableLoop && mAdapter.getCount() != 0) {\n                realPosition = position % mAdapter.getCount();\n            }\n            mAdapter.destroyItem(container, realPosition, object);\n        }\n\n        @Override\n        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {\n            return mAdapter.isViewFromObject(view, object);\n        }\n\n\n        @Override\n        public void restoreState(Parcelable bundle, ClassLoader classLoader) {\n            mAdapter.restoreState(bundle, classLoader);\n        }\n\n        @Override\n        public Parcelable saveState() {\n            return mAdapter.saveState();\n        }\n\n        @Override\n        public void startUpdate(@NonNull ViewGroup container) {\n            mAdapter.startUpdate(container);\n        }\n\n        @Override\n        public void finishUpdate(@NonNull ViewGroup container) {\n            mAdapter.finishUpdate(container);\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            int virtualPosition = position % mAdapter.getCount();\n            return mAdapter.getPageTitle(virtualPosition);\n        }\n\n        @Override\n        public float getPageWidth(int position) {\n            return mAdapter.getPageWidth(position);\n        }\n\n        @Override\n        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {\n            mAdapter.setPrimaryItem(container, position, object);\n        }\n\n        @Override\n        public void unregisterDataSetObserver(@NonNull DataSetObserver observer) {\n            mAdapter.unregisterDataSetObserver(observer);\n        }\n\n        @Override\n        public void registerDataSetObserver(@NonNull DataSetObserver observer) {\n            mAdapter.registerDataSetObserver(observer);\n        }\n\n        @Override\n        public void notifyDataSetChanged() {\n            super.notifyDataSetChanged();\n            mAdapter.notifyDataSetChanged();\n        }\n\n        @Override\n        public int getItemPosition(@NonNull Object object) {\n            return mAdapter.getItemPosition(object);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIWindowInsetLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\n@Deprecated\npublic class QMUIWindowInsetLayout extends QMUIFrameLayout {\n\n    public QMUIWindowInsetLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIWindowInsetLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIWindowInsetLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void onViewAdded(View child) {\n        super.onViewAdded(child);\n        QMUIWindowInsetHelper.handleWindowInsets(child, WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout());\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIWindowInsetLayout2.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\n@Deprecated\npublic class QMUIWindowInsetLayout2 extends QMUIConstraintLayout {\n    public QMUIWindowInsetLayout2(Context context) {\n        this(context, null);\n    }\n\n    public QMUIWindowInsetLayout2(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIWindowInsetLayout2(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void setFitsSystemWindows(boolean fitSystemWindows) {\n        // do nothing.\n    }\n\n    @Override\n    public void onViewAdded(View view) {\n        super.onViewAdded(view);\n        QMUIWindowInsetHelper.handleWindowInsets(view, WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout());\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIWrapContentListView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.ListView;\n\n/**\n * 支持高度值为 wrap_content 的 {@link ListView}，解决原生 {@link ListView} 在设置高度为 wrap_content 时高度计算错误的 bug。\n */\n\npublic class QMUIWrapContentListView extends ListView {\n    private int mMaxHeight = Integer.MAX_VALUE >> 2;\n\n    public QMUIWrapContentListView(Context context){\n        super(context);\n    }\n\n    public QMUIWrapContentListView(Context context, int maxHeight) {\n        super(context);\n        mMaxHeight = maxHeight;\n    }\n\n    public QMUIWrapContentListView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIWrapContentListView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public void setMaxHeight(int maxHeight) {\n        if(mMaxHeight != maxHeight){\n            mMaxHeight = maxHeight;\n            requestLayout();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int expandSpec = MeasureSpec.makeMeasureSpec(mMaxHeight,\n                MeasureSpec.AT_MOST);\n        super.onMeasure(widthMeasureSpec, expandSpec);\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/QMUIWrapContentScrollView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\n\n/**\n * height is wrapContent but limited by maxHeight\n * <p>\n * Created by cgspine on 2017/12/21.\n */\n\npublic class QMUIWrapContentScrollView extends QMUIObservableScrollView {\n    private int mMaxHeight = Integer.MAX_VALUE >> 2;\n\n    public QMUIWrapContentScrollView(Context context) {\n        super(context);\n    }\n\n    public QMUIWrapContentScrollView(Context context, int maxHeight) {\n        super(context);\n        mMaxHeight = maxHeight;\n    }\n\n    public QMUIWrapContentScrollView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUIWrapContentScrollView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public void setMaxHeight(int maxHeight) {\n        if (mMaxHeight != maxHeight) {\n            mMaxHeight = maxHeight;\n            requestLayout();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        ViewGroup.LayoutParams lp = getLayoutParams();\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        int maxHeight = Math.min(heightSize, mMaxHeight);\n        int expandSpec;\n        if (lp != null && lp.height > 0 && lp.height <= mMaxHeight) {\n            expandSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);\n        } else {\n            expandSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);\n        }\n\n        super.onMeasure(widthMeasureSpec, expandSpec);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBaseDialog.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.content.res.TypedArray;\nimport android.view.LayoutInflater;\nimport android.view.Window;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatDialog;\nimport androidx.core.view.LayoutInflaterCompat;\n\nimport com.qmuiteam.qmui.skin.QMUISkinLayoutInflaterFactory;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\n\npublic class QMUIBaseDialog extends AppCompatDialog {\n    boolean cancelable = true;\n    private boolean canceledOnTouchOutside = true;\n    private boolean canceledOnTouchOutsideSet;\n    private QMUISkinManager mSkinManager = null;\n\n    public QMUIBaseDialog(@NonNull Context context, int themeResId) {\n        super(context, themeResId);\n        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);\n    }\n\n    public void setSkinManager(@Nullable QMUISkinManager skinManager) {\n        if(mSkinManager != null){\n            mSkinManager.unRegister(this);\n        }\n        mSkinManager = skinManager;\n        if(isShowing() && skinManager != null){\n            mSkinManager.register(this);\n        }\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        if (mSkinManager != null) {\n            mSkinManager.register(this);\n        }\n    }\n\n    @NonNull\n    @Override\n    public LayoutInflater getLayoutInflater() {\n        LayoutInflater layoutInflater = super.getLayoutInflater();\n        LayoutInflater.Factory2 factory2 = layoutInflater.getFactory2();\n        if(factory2 instanceof QMUISkinLayoutInflaterFactory){\n            LayoutInflaterCompat.setFactory2(layoutInflater,\n                    ((QMUISkinLayoutInflaterFactory)factory2).cloneForLayoutInflaterIfNeeded(layoutInflater));\n        }\n        return layoutInflater;\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        if (mSkinManager != null) {\n            mSkinManager.unRegister(this);\n        }\n    }\n\n    @Override\n    public void setCancelable(boolean cancelable) {\n        super.setCancelable(cancelable);\n        if (this.cancelable != cancelable) {\n            this.cancelable = cancelable;\n            onSetCancelable(cancelable);\n        }\n    }\n\n    protected void onSetCancelable(boolean cancelable) {\n\n    }\n\n    @Override\n    public void setCanceledOnTouchOutside(boolean cancel) {\n        super.setCanceledOnTouchOutside(cancel);\n        if (cancel && !cancelable) {\n            cancelable = true;\n        }\n        canceledOnTouchOutside = cancel;\n        canceledOnTouchOutsideSet = true;\n    }\n\n    protected boolean shouldWindowCloseOnTouchOutside() {\n        if (!canceledOnTouchOutsideSet) {\n            TypedArray a =\n                    getContext()\n                            .obtainStyledAttributes(new int[]{android.R.attr.windowCloseOnTouchOutside});\n            canceledOnTouchOutside = a.getBoolean(0, true);\n            a.recycle();\n            canceledOnTouchOutsideSet = true;\n        }\n        return canceledOnTouchOutside;\n    }\n\n    @Override\n    public void dismiss() {\n        Context context = getContext();\n        if(context instanceof ContextWrapper){\n            context = ((ContextWrapper)context).getBaseContext();\n        }\n        if(context instanceof Activity){\n            Activity activity = (Activity) context;\n            if(activity.isDestroyed() || activity.isFinishing()){\n                return;\n            }\n            super.dismiss();\n        }else{\n            try{\n                super.dismiss();\n            }catch (Throwable ignore){\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheet.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport static com.qmuiteam.qmui.layout.IQMUILayout.HIDE_RADIUS_SIDE_BOTTOM;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.util.Pair;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.bottomsheet.BottomSheetBehavior;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * QMUIBottomSheet 在 {@link Dialog} 的基础上重新定制了 {@link #show()} 和 {@link #hide()} 时的动画效果, 使 {@link Dialog} 在界面底部升起和降下。\n * <p>\n * 提供了以下两种面板样式:\n * <ul>\n * <li>列表样式, 使用 {@link QMUIBottomSheet.BottomListSheetBuilder} 生成。</li>\n * <li>宫格类型, 使用 {@link QMUIBottomSheet.BottomGridSheetBuilder} 生成。</li>\n * </ul>\n * </p>\n */\npublic class QMUIBottomSheet extends QMUIBaseDialog {\n    private static final String TAG = \"QMUIBottomSheet\";\n    private QMUIBottomSheetRootLayout mRootView;\n    private OnBottomSheetShowListener mOnBottomSheetShowListener;\n    private QMUIBottomSheetBehavior<QMUIBottomSheetRootLayout> mBehavior;\n    private boolean mAnimateToCancel = false;\n    private boolean mAnimateToDismiss = false;\n\n\n    public QMUIBottomSheet(Context context) {\n        this(context, R.style.QMUI_BottomSheet);\n    }\n\n    public QMUIBottomSheet(Context context, int style) {\n        super(context, style);\n        ViewGroup container = (ViewGroup) getLayoutInflater().inflate(R.layout.qmui_bottom_sheet_dialog, null);\n        mRootView = container.findViewById(R.id.bottom_sheet);\n        mBehavior = new QMUIBottomSheetBehavior<>();\n        mBehavior.setHideable(cancelable);\n        mBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {\n            @Override\n            public void onStateChanged(@NonNull View bottomSheet, int newState) {\n                if (newState == BottomSheetBehavior.STATE_HIDDEN) {\n                    if (mAnimateToCancel) {\n                        // cancel() invoked\n                        cancel();\n                    } else if (mAnimateToDismiss) {\n                        // dismiss() invoked but it it not triggered by cancel()\n                        dismiss();\n                    } else {\n                        // drag to cancel\n                        cancel();\n                    }\n                }\n            }\n\n            @Override\n            public void onSlide(@NonNull View bottomSheet, float slideOffset) {\n\n            }\n        });\n        mBehavior.setPeekHeight(0);\n        mBehavior.setAllowDrag(false);\n        mBehavior.setSkipCollapsed(true);\n        CoordinatorLayout.LayoutParams rootViewLp = (CoordinatorLayout.LayoutParams) mRootView.getLayoutParams();\n        rootViewLp.setBehavior(mBehavior);\n\n        // We treat the CoordinatorLayout as outside the dialog though it is technically inside\n        container.findViewById(R.id.touch_outside)\n                .setOnClickListener(\n                        new View.OnClickListener() {\n                            @Override\n                            public void onClick(View view) {\n                                if(mBehavior.getState() == BottomSheetBehavior.STATE_SETTLING){\n                                    return;\n                                }\n                                if (cancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {\n                                    cancel();\n                                }\n                            }\n                        });\n        mRootView.setOnTouchListener(\n                new View.OnTouchListener() {\n                    @Override\n                    public boolean onTouch(View view, MotionEvent event) {\n                        // Consume the event and prevent it from falling through\n                        return true;\n                    }\n                });\n\n        super.setContentView(container, new ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n    }\n\n    @Override\n    protected void onSetCancelable(boolean cancelable) {\n        super.onSetCancelable(cancelable);\n        mBehavior.setHideable(cancelable);\n    }\n\n    public void setFitNav(boolean fitNav) {\n        if(fitNav){\n            mRootView.setFitsSystemWindows(true);\n            QMUIWindowInsetHelper.handleWindowInsets(mRootView,\n                    WindowInsetsCompat.Type.navigationBars(),\n                    getInsetHandler(),\n                    true, true, false);\n        }else{\n            mRootView.setFitsSystemWindows(false);\n            QMUIWindowInsetHelper.setOnApplyWindowInsetsListener(mRootView, null, true);\n        }\n        mRootView.requestApplyInsets();\n    }\n\n    protected QMUIWindowInsetHelper.InsetHandler getInsetHandler(){\n        return QMUIWindowInsetHelper.consumeInsetWithPaddingHandler;\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Window window = getWindow();\n        if (window != null) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n        }\n        ViewCompat.requestApplyInsets(mRootView);\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        if (mBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {\n            mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);\n        }\n    }\n\n    @Override\n    public void cancel() {\n        if (mBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {\n            mAnimateToCancel = false;\n            super.cancel();\n        } else {\n            mAnimateToCancel = true;\n            mBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);\n        }\n    }\n\n    @Override\n    public void dismiss() {\n        if (mBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {\n            mAnimateToDismiss = false;\n            super.dismiss();\n        } else {\n            mAnimateToDismiss = true;\n            mBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);\n        }\n    }\n\n    public void setOnBottomSheetShowListener(OnBottomSheetShowListener onBottomSheetShowListener) {\n        mOnBottomSheetShowListener = onBottomSheetShowListener;\n    }\n\n    public void setRadius(int radius) {\n        mRootView.setRadius(radius, HIDE_RADIUS_SIDE_BOTTOM);\n    }\n\n    public QMUIBottomSheetRootLayout getRootView() {\n        return mRootView;\n    }\n\n    public QMUIBottomSheetBehavior<QMUIBottomSheetRootLayout> getBehavior() {\n        return mBehavior;\n    }\n\n    @Override\n    public void show() {\n        super.show();\n        if (mOnBottomSheetShowListener != null) {\n            mOnBottomSheetShowListener.onShow();\n        }\n        if (mBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {\n            setToExpandWhenShow();\n        }\n        mAnimateToCancel = false;\n        mAnimateToDismiss = false;\n    }\n\n    protected void setToExpandWhenShow(){\n        mRootView.postOnAnimation(new Runnable() {\n            @Override\n            public void run() {\n                mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);\n            }\n        });\n    }\n\n    public interface OnBottomSheetShowListener {\n        void onShow();\n    }\n\n    @Override\n    public void setContentView(View view) {\n        throw new IllegalStateException(\n                \"Use addContentView(View, ConstraintLayout.LayoutParams) for replacement\");\n    }\n\n    @Override\n    public void setContentView(int layoutResId) {\n        throw new IllegalStateException(\n                \"Use addContentView(int) for replacement\");\n    }\n\n    @Override\n    public void setContentView(View view, ViewGroup.LayoutParams params) {\n        throw new IllegalStateException(\n                \"Use addContentView(View, QMUIPriorityLinearLayout.LayoutParams) for replacement\");\n    }\n\n    @Override\n    public void addContentView(View view, ViewGroup.LayoutParams params) {\n        throw new IllegalStateException(\n                \"Use addContentView(View, QMUIPriorityLinearLayout.LayoutParams) for replacement\");\n    }\n\n    public void addContentView(View view, QMUIPriorityLinearLayout.LayoutParams layoutParams) {\n        mRootView.addView(view, layoutParams);\n    }\n\n    public void addContentView(View view) {\n        QMUIPriorityLinearLayout.LayoutParams lp =  new QMUIPriorityLinearLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.setPriority(QMUIPriorityLinearLayout.LayoutParams.PRIORITY_DISPOSABLE);\n        mRootView.addView(view, lp);\n    }\n\n    public void addContentView(int layoutResId) {\n        LayoutInflater.from(mRootView.getContext()).inflate(layoutResId, mRootView, true);\n    }\n\n\n    /**\n     * 生成列表类型的 {@link QMUIBottomSheet} 对话框。\n     */\n    public static class BottomListSheetBuilder extends QMUIBottomSheetBaseBuilder<BottomListSheetBuilder> {\n\n\n        private List<QMUIBottomSheetListItemModel> mItems;\n        private List<View> mContentHeaderViews;\n        private List<View> mContentFooterViews;\n        private boolean mNeedRightMark; //是否需要rightMark,标识当前项\n        private int mCheckedIndex;\n        private boolean mGravityCenter = false;\n        private OnSheetItemClickListener mOnSheetItemClickListener;\n\n\n        public BottomListSheetBuilder(Context context) {\n            this(context, false);\n        }\n\n        /**\n         * @param needRightMark 是否需要在被选中的 Item 右侧显示一个勾(使用 {@link #setCheckedIndex(int)} 设置选中的 Item)\n         */\n        public BottomListSheetBuilder(Context context, boolean needRightMark) {\n            super(context);\n            mItems = new ArrayList<>();\n            mNeedRightMark = needRightMark;\n        }\n\n        /**\n         * 设置要被选中的 Item 的下标。\n         * <p>\n         * 注意:仅当 {@link #mNeedRightMark} 为 true 时才有效。\n         */\n        public BottomListSheetBuilder setCheckedIndex(int checkedIndex) {\n            mCheckedIndex = checkedIndex;\n            return this;\n        }\n\n        public BottomListSheetBuilder setNeedRightMark(boolean needRightMark) {\n            mNeedRightMark = needRightMark;\n            return this;\n        }\n\n        public BottomListSheetBuilder setGravityCenter(boolean gravityCenter) {\n            mGravityCenter = gravityCenter;\n            return this;\n        }\n\n        public BottomListSheetBuilder setOnSheetItemClickListener(\n                OnSheetItemClickListener onSheetItemClickListener) {\n            mOnSheetItemClickListener = onSheetItemClickListener;\n            return this;\n        }\n\n        public BottomListSheetBuilder addItem(QMUIBottomSheetListItemModel itemModel) {\n            mItems.add(itemModel);\n            return this;\n        }\n\n        /**\n         * @param textAndTag Item 的文字内容，同时会把内容设置为 tag。\n         */\n        public BottomListSheetBuilder addItem(String textAndTag) {\n            mItems.add(new QMUIBottomSheetListItemModel(textAndTag, textAndTag));\n            return this;\n        }\n\n        /**\n         * @param image      icon Item 的 icon。\n         * @param textAndTag Item 的文字内容，同时会把内容设置为 tag。\n         */\n        public BottomListSheetBuilder addItem(Drawable image, String textAndTag) {\n            mItems.add(new QMUIBottomSheetListItemModel(textAndTag, textAndTag).image(image));\n            return this;\n        }\n\n        /**\n         * @param text Item 的文字内容。\n         * @param tag  item 的 tag。\n         */\n        public BottomListSheetBuilder addItem(String text, String tag) {\n            mItems.add(new QMUIBottomSheetListItemModel(text, tag));\n            return this;\n        }\n\n        /**\n         * @param imageRes Item 的图标 Resource。\n         * @param text     Item 的文字内容。\n         * @param tag      Item 的 tag。\n         */\n        public BottomListSheetBuilder addItem(int imageRes, String text, String tag) {\n            mItems.add(new QMUIBottomSheetListItemModel(text, tag).image(imageRes));\n            return this;\n        }\n\n        /**\n         * @param imageRes    Item 的图标 Resource。\n         * @param text        Item 的文字内容。\n         * @param tag         Item 的 tag。\n         * @param hasRedPoint 是否显示红点。\n         */\n        public BottomListSheetBuilder addItem(int imageRes, String text, String tag, boolean hasRedPoint) {\n            mItems.add(new QMUIBottomSheetListItemModel(text, tag).image(imageRes).redPoint(hasRedPoint));\n            return this;\n        }\n\n        /**\n         * @param imageRes    Item 的图标 Resource。\n         * @param text        Item 的文字内容。\n         * @param tag         Item 的 tag。\n         * @param hasRedPoint 是否显示红点。\n         * @param disabled    是否显示禁用态。\n         */\n        public BottomListSheetBuilder addItem(\n                int imageRes, CharSequence text, String tag, boolean hasRedPoint, boolean disabled) {\n            mItems.add(new QMUIBottomSheetListItemModel(text, tag)\n                    .image(imageRes).redPoint(hasRedPoint).disabled(disabled));\n            return this;\n        }\n\n\n        @Deprecated\n        public BottomListSheetBuilder addHeaderView(@NonNull View view) {\n            return addContentHeaderView(view);\n        }\n\n        public BottomListSheetBuilder addContentHeaderView(@NonNull View view) {\n            if (mContentHeaderViews == null) {\n                mContentHeaderViews = new ArrayList<>();\n            }\n            mContentHeaderViews.add(view);\n            return this;\n        }\n\n        public BottomListSheetBuilder addContentFooterView(@NonNull View view) {\n            if (mContentFooterViews == null) {\n                mContentFooterViews = new ArrayList<>();\n            }\n            mContentFooterViews.add(view);\n            return this;\n        }\n\n\n        @Nullable\n        @Override\n        protected View onCreateContentView(@NonNull final QMUIBottomSheet bottomSheet,\n                                           @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                           @NonNull Context context) {\n            RecyclerView recyclerView = new RecyclerView(context);\n            recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);\n            QMUIBottomSheetListAdapter adapter = new QMUIBottomSheetListAdapter(\n                    mNeedRightMark, mGravityCenter);\n            recyclerView.setAdapter(adapter);\n            recyclerView.setLayoutManager(new LinearLayoutManager(context) {\n                @Override\n                public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                    return new RecyclerView.LayoutParams(\n                            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n                }\n            });\n            recyclerView.addItemDecoration(new QMUIBottomSheetListItemDecoration(context));\n\n            LinearLayout headerView = null;\n            if (mContentHeaderViews != null && mContentHeaderViews.size() > 0) {\n                headerView = new LinearLayout(context);\n                headerView.setOrientation(LinearLayout.VERTICAL);\n                for (View view : mContentHeaderViews) {\n                    if (view.getParent() != null) {\n                        ((ViewGroup) view.getParent()).removeView(view);\n                    }\n                    headerView.addView(view, new LinearLayout.LayoutParams(\n                            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n                }\n            }\n            LinearLayout footerView = null;\n            if (mContentFooterViews != null && mContentFooterViews.size() > 0) {\n                footerView = new LinearLayout(context);\n                footerView.setOrientation(LinearLayout.VERTICAL);\n                for (View view : mContentFooterViews) {\n                    if (view.getParent() != null) {\n                        ((ViewGroup) view.getParent()).removeView(view);\n                    }\n                    footerView.addView(view, new LinearLayout.LayoutParams(\n                            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n                }\n            }\n            adapter.setData(headerView, footerView, mItems);\n            adapter.setOnItemClickListener(new QMUIBottomSheetListAdapter.OnItemClickListener() {\n                @Override\n                public void onClick(QMUIBottomSheetListAdapter.VH vh, int dataPos, QMUIBottomSheetListItemModel model) {\n                    if (mOnSheetItemClickListener != null) {\n                        mOnSheetItemClickListener.onClick(bottomSheet, vh.itemView, dataPos, model.tag);\n                    }\n                }\n            });\n            adapter.setCheckedIndex(mCheckedIndex);\n            recyclerView.scrollToPosition(mCheckedIndex + (headerView == null ? 0 : 1));\n            return recyclerView;\n        }\n\n\n        public interface OnSheetItemClickListener {\n            void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag);\n        }\n    }\n\n    /**\n     * 生成宫格类型的 {@link QMUIBottomSheet} 对话框。\n     */\n    public static class BottomGridSheetBuilder extends QMUIBottomSheetBaseBuilder<BottomGridSheetBuilder>\n            implements View.OnClickListener {\n\n        public static final int FIRST_LINE = 0;\n        public static final int SECOND_LINE = 1;\n        public static final ItemViewFactory DEFAULT_ITEM_VIEW_FACTORY = new DefaultItemViewFactory();\n\n        public interface ItemViewFactory {\n            QMUIBottomSheetGridItemView create(QMUIBottomSheet bottomSheet, QMUIBottomSheetGridItemModel model);\n        }\n\n        public static class DefaultItemViewFactory implements ItemViewFactory {\n\n            @Override\n            public QMUIBottomSheetGridItemView create(@NonNull QMUIBottomSheet bottomSheet, @NonNull QMUIBottomSheetGridItemModel model) {\n                QMUIBottomSheetGridItemView itemView = new QMUIBottomSheetGridItemView(bottomSheet.getContext());\n                itemView.render(model);\n                return itemView;\n            }\n        }\n\n        private ArrayList<QMUIBottomSheetGridItemModel> mFirstLineItems;\n        private ArrayList<QMUIBottomSheetGridItemModel> mSecondLineItems;\n        private ItemViewFactory mItemViewFactory = DEFAULT_ITEM_VIEW_FACTORY;\n        private OnSheetItemClickListener mOnSheetItemClickListener;\n        private QMUIBottomSheetGridLineLayout.ItemWidthCalculator mItemWidthCalculator = null;\n        private int mLineGravity = Gravity.CENTER_VERTICAL;\n\n        public BottomGridSheetBuilder(Context context) {\n            super(context);\n            mFirstLineItems = new ArrayList<>();\n            mSecondLineItems = new ArrayList<>();\n        }\n\n        public BottomGridSheetBuilder setLineGravity(int gravity){\n            mLineGravity = gravity;\n            return this;\n        }\n\n        public BottomGridSheetBuilder addItem(@NonNull QMUIBottomSheetGridItemModel model, @Style int style) {\n            switch (style) {\n                case FIRST_LINE:\n                    mFirstLineItems.add(model);\n                    break;\n                case SECOND_LINE:\n                    mSecondLineItems.add(model);\n                    break;\n            }\n            return this;\n        }\n\n        public BottomGridSheetBuilder addItem(int imageRes, CharSequence textAndTag, @Style int style) {\n            return addItem(imageRes, textAndTag, textAndTag, style, 0);\n        }\n\n        public BottomGridSheetBuilder addItem(int imageRes, CharSequence text, Object tag, @Style int style) {\n            return addItem(imageRes, text, tag, style, 0);\n        }\n\n        public BottomGridSheetBuilder addItem(int imageRes, CharSequence text, Object tag,\n                                              @Style int style, int subscriptRes) {\n            return addItem(imageRes, text, tag, style, subscriptRes, null);\n        }\n\n        public BottomGridSheetBuilder addItem(int imageRes, CharSequence text, Object tag,\n                                              @Style int style, int subscriptRes, Typeface typeface) {\n            return addItem(new QMUIBottomSheetGridItemModel(text, tag)\n                    .image(imageRes)\n                    .subscript(subscriptRes)\n                    .typeface(typeface), style);\n        }\n\n\n        public void setItemViewFactory(ItemViewFactory itemViewFactory) {\n            mItemViewFactory = itemViewFactory;\n        }\n\n        public BottomGridSheetBuilder setOnSheetItemClickListener(OnSheetItemClickListener onSheetItemClickListener) {\n            mOnSheetItemClickListener = onSheetItemClickListener;\n            return this;\n        }\n\n        public BottomGridSheetBuilder setItemWidthCalculator(QMUIBottomSheetGridLineLayout.ItemWidthCalculator itemWidthCalculator) {\n            mItemWidthCalculator = itemWidthCalculator;\n            return this;\n        }\n\n        @Override\n        public void onClick(View v) {\n            if (mOnSheetItemClickListener != null) {\n                mOnSheetItemClickListener.onClick(mDialog, v);\n            }\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContentView(@NonNull QMUIBottomSheet bottomSheet,\n                                           @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                           @NonNull Context context) {\n            if (mFirstLineItems.isEmpty() && mSecondLineItems.isEmpty()) {\n                return null;\n            }\n            List<Pair<View, LinearLayout.LayoutParams>> firstLines = null;\n            List<Pair<View, LinearLayout.LayoutParams>> secondLines = null;\n            int wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT;\n\n            if (!mFirstLineItems.isEmpty()) {\n                firstLines = new ArrayList<>();\n                for (QMUIBottomSheetGridItemModel model : mFirstLineItems) {\n                    QMUIBottomSheetGridItemView itemView = mItemViewFactory.create(bottomSheet, model);\n                    itemView.setOnClickListener(this);\n                    firstLines.add(new Pair<View, LinearLayout.LayoutParams>(\n                            itemView,\n                            new LinearLayout.LayoutParams(wrapContent, wrapContent)));\n                }\n            }\n            if (!mSecondLineItems.isEmpty()) {\n                secondLines = new ArrayList<>();\n                for (QMUIBottomSheetGridItemModel model : mSecondLineItems) {\n                    QMUIBottomSheetGridItemView itemView = mItemViewFactory.create(bottomSheet, model);\n                    itemView.setOnClickListener(this);\n                    secondLines.add(new Pair<View, LinearLayout.LayoutParams>(\n                            itemView,\n                            new LinearLayout.LayoutParams(wrapContent, wrapContent)));\n                }\n            }\n            return new QMUIBottomSheetGridLineLayout(mDialog, mItemWidthCalculator, mLineGravity, firstLines, secondLines);\n        }\n\n        public interface OnSheetItemClickListener {\n            void onClick(QMUIBottomSheet dialog, View itemView);\n        }\n\n        @Retention(RetentionPolicy.SOURCE)\n        @IntDef({FIRST_LINE, SECOND_LINE})\n        public @interface Style {\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetBaseBuilder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIButton;\nimport com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\npublic abstract class QMUIBottomSheetBaseBuilder<T extends QMUIBottomSheetBaseBuilder> {\n    private Context mContext;\n    protected QMUIBottomSheet mDialog;\n    private CharSequence mTitle;\n    private boolean mAddCancelBtn;\n    private String mCancelText;\n    private DialogInterface.OnDismissListener mOnBottomDialogDismissListener;\n    private int mRadius = -1;\n    private boolean mAllowDrag = false;\n    private QMUISkinManager mSkinManager;\n    private QMUIBottomSheetBehavior.DownDragDecisionMaker mDownDragDecisionMaker = null;\n    private boolean fitNav = true;\n\n    public QMUIBottomSheetBaseBuilder(Context context) {\n        mContext = context;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setTitle(CharSequence title) {\n        mTitle = title;\n        return (T) this;\n    }\n\n    protected boolean hasTitle() {\n        return mTitle != null && mTitle.length() != 0;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setAllowDrag(boolean allowDrag) {\n        mAllowDrag = allowDrag;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setSkinManager(@Nullable QMUISkinManager skinManager) {\n        mSkinManager = skinManager;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setFitNav(boolean fitNav) {\n        this.fitNav = fitNav;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setDownDragDecisionMaker(QMUIBottomSheetBehavior.DownDragDecisionMaker downDragDecisionMaker) {\n        mDownDragDecisionMaker = downDragDecisionMaker;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setAddCancelBtn(boolean addCancelBtn) {\n        mAddCancelBtn = addCancelBtn;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setCancelText(String cancelText) {\n        mCancelText = cancelText;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setRadius(int radius) {\n        mRadius = radius;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setOnBottomDialogDismissListener(DialogInterface.OnDismissListener listener) {\n        mOnBottomDialogDismissListener = listener;\n        return (T) this;\n    }\n\n    public QMUIBottomSheet build() {\n        return build(R.style.QMUI_BottomSheet);\n    }\n\n    public QMUIBottomSheet build(int style) {\n        mDialog = new QMUIBottomSheet(mContext, style);\n        Context dialogContext = mDialog.getContext();\n        QMUIBottomSheetRootLayout rootLayout = mDialog.getRootView();\n        rootLayout.removeAllViews();\n        View titleView = onCreateTitleView(mDialog, rootLayout, dialogContext);\n        if (titleView != null) {\n            mDialog.addContentView(titleView);\n        }\n        onAddCustomViewBetweenTitleAndContent(mDialog, rootLayout, dialogContext);\n        View contentView = onCreateContentView(mDialog, rootLayout, dialogContext);\n        if (contentView != null) {\n            QMUIPriorityLinearLayout.LayoutParams lp = new QMUIPriorityLinearLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            lp.setPriority(QMUIPriorityLinearLayout.LayoutParams.PRIORITY_DISPOSABLE);\n            mDialog.addContentView(contentView, lp);\n        }\n        onAddCustomViewAfterContent(mDialog, rootLayout, dialogContext);\n\n        if (mAddCancelBtn) {\n            mDialog.addContentView(onCreateCancelBtn(mDialog, rootLayout, dialogContext),\n                    new QMUIPriorityLinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                            QMUIResHelper.getAttrDimen(dialogContext,\n                                    R.attr.qmui_bottom_sheet_cancel_btn_height)));\n        }\n\n        if (mOnBottomDialogDismissListener != null) {\n            mDialog.setOnDismissListener(mOnBottomDialogDismissListener);\n        }\n        if (mRadius != -1) {\n            mDialog.setRadius(mRadius);\n        }\n        mDialog.setSkinManager(mSkinManager);\n        mDialog.setFitNav(fitNav);\n\n        QMUIBottomSheetBehavior behavior = mDialog.getBehavior();\n        behavior.setAllowDrag(mAllowDrag);\n        behavior.setDownDragDecisionMaker(mDownDragDecisionMaker);\n        return mDialog;\n    }\n\n\n    @Nullable\n    protected View onCreateTitleView(@NonNull QMUIBottomSheet bottomSheet,\n                                     @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                     @NonNull Context context) {\n        if (hasTitle()) {\n            QMUISpanTouchFixTextView tv = new QMUISpanTouchFixTextView(context);\n            tv.setId(R.id.qmui_bottom_sheet_title);\n            tv.setText(mTitle);\n            tv.onlyShowBottomDivider(0, 0, 1,\n                    QMUIResHelper.getAttrColor(context, R.attr.qmui_skin_support_bottom_sheet_separator_color));\n            QMUIResHelper.assignTextViewWithAttr(tv, R.attr.qmui_bottom_sheet_title_style);\n            QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n\n            valueBuilder.textColor(R.attr.qmui_skin_support_bottom_sheet_title_text_color);\n            valueBuilder.bottomSeparator(R.attr.qmui_skin_support_bottom_sheet_separator_color);\n            QMUISkinHelper.setSkinValue(tv, valueBuilder);\n            valueBuilder.release();\n            return tv;\n        }\n        return null;\n    }\n\n    protected void onAddCustomViewBetweenTitleAndContent(@NonNull QMUIBottomSheet bottomSheet,\n                                                         @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                                         @NonNull Context context) {\n    }\n\n    @Nullable\n    protected abstract View onCreateContentView(@NonNull QMUIBottomSheet bottomSheet,\n                                                @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                                @NonNull Context context);\n\n    protected void onAddCustomViewAfterContent(@NonNull QMUIBottomSheet bottomSheet,\n                                               @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                               @NonNull Context context) {\n    }\n\n    @NonNull\n    protected View onCreateCancelBtn(@NonNull final QMUIBottomSheet bottomSheet,\n                                     @NonNull QMUIBottomSheetRootLayout rootLayout,\n                                     @NonNull Context context) {\n        QMUIButton button = new QMUIButton(context);\n        button.setId(R.id.qmui_bottom_sheet_cancel);\n        if (mCancelText == null || mCancelText.isEmpty()) {\n            mCancelText = context.getString(R.string.qmui_cancel);\n        }\n        button.setPadding(0, 0,0, 0);\n        button.setBackground(QMUIResHelper.getAttrDrawable(\n                context, R.attr.qmui_skin_support_bottom_sheet_cancel_bg));\n        button.setText(mCancelText);\n        QMUIResHelper.assignTextViewWithAttr(button, R.attr.qmui_bottom_sheet_cancel_style);\n        button.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                bottomSheet.cancel();\n            }\n        });\n        button.onlyShowTopDivider(0, 0, 1,\n                QMUIResHelper.getAttrColor(\n                        context, R.attr.qmui_skin_support_bottom_sheet_separator_color));\n\n        QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n        valueBuilder.textColor(R.attr.qmui_skin_support_bottom_sheet_cancel_text_color);\n        valueBuilder.topSeparator(R.attr.qmui_skin_support_bottom_sheet_separator_color);\n        valueBuilder.background(R.attr.qmui_skin_support_bottom_sheet_cancel_bg);\n        QMUISkinHelper.setSkinValue(button, valueBuilder);\n        valueBuilder.release();\n        return button;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetBehavior.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\nimport com.google.android.material.bottomsheet.BottomSheetBehavior;\n\npublic class QMUIBottomSheetBehavior<V extends ViewGroup> extends BottomSheetBehavior<V> {\n    private boolean mAllowDrag = true;\n    private boolean mMotionEventCanDrag = true;\n    private DownDragDecisionMaker mDownDragDecisionMaker;\n\n    public void setAllowDrag(boolean allowDrag) {\n        mAllowDrag = allowDrag;\n    }\n\n    public void setDownDragDecisionMaker(DownDragDecisionMaker downDragDecisionMaker) {\n        mDownDragDecisionMaker = downDragDecisionMaker;\n    }\n\n    @Override\n    public boolean onTouchEvent(@NonNull CoordinatorLayout parent,\n                                @NonNull V child,\n                                @NonNull MotionEvent event) {\n        if(!mAllowDrag){\n            return false;\n        }\n\n        if(event.getAction() == MotionEvent.ACTION_DOWN){\n            mMotionEventCanDrag = mDownDragDecisionMaker == null ||\n                    mDownDragDecisionMaker.canDrag(parent, child, event);\n        }\n\n        if(!mMotionEventCanDrag){\n            return false;\n        }\n\n        return super.onTouchEvent(parent, child, event);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent,\n                                         @NonNull V child,\n                                         @NonNull MotionEvent event) {\n        if(!mAllowDrag){\n            return false;\n        }\n\n        if(event.getAction() == MotionEvent.ACTION_DOWN){\n            mMotionEventCanDrag = mDownDragDecisionMaker == null ||\n                    mDownDragDecisionMaker.canDrag(parent, child, event);\n        }\n        if(!mMotionEventCanDrag){\n            return false;\n        }\n        return super.onInterceptTouchEvent(parent, child, event);\n    }\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,\n                                       @NonNull V child,\n                                       @NonNull View directTargetChild,\n                                       @NonNull View target, int axes, int type) {\n        if(!mAllowDrag){\n            return false;\n        }\n        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);\n    }\n\n\n    public interface DownDragDecisionMaker {\n        boolean canDrag(@NonNull CoordinatorLayout parent,\n                        @NonNull View child,\n                        @NonNull MotionEvent event);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetGridItemModel.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\n\npublic class QMUIBottomSheetGridItemModel {\n    Drawable image = null;\n    int imageRes = 0;\n    int imageSkinTintColorAttr = 0;\n    int imageSkinSrcAttr = 0;\n    int textSkinColorAttr = 0;\n    CharSequence text;\n    Object tag = \"\";\n    Drawable subscript = null;\n    int subscriptRes = 0;\n    int subscriptSkinTintColorAttr = 0;\n    int subscriptSkinSrcAttr = 0;\n    Typeface typeface;\n\n    public QMUIBottomSheetGridItemModel(CharSequence text, Object tag) {\n        this.text = text;\n        this.tag = tag;\n    }\n\n    public QMUIBottomSheetGridItemModel image(Drawable image) {\n        this.image = image;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel image(int imageRes) {\n        this.imageRes = imageRes;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel subscript(Drawable image) {\n        this.subscript = image;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel subscript(int imageRes) {\n        this.subscriptRes = imageRes;\n        return this;\n    }\n\n\n    public QMUIBottomSheetGridItemModel skinTextColorAttr(int attr) {\n        this.textSkinColorAttr = attr;\n        return this;\n    }\n\n\n    public QMUIBottomSheetGridItemModel skinImageTintColorAttr(int attr) {\n        this.imageSkinTintColorAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel skinImageSrcAttr(int attr) {\n        this.imageSkinSrcAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel skinSubscriptTintColorAttr(int attr) {\n        this.subscriptSkinTintColorAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel skinSubscriptSrcAttr(int attr) {\n        this.subscriptSkinSrcAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetGridItemModel typeface(Typeface typeface) {\n        this.typeface = typeface;\n        return this;\n    }\n\n    public CharSequence getText() {\n        return text;\n    }\n\n    public Drawable getImage() {\n        return image;\n    }\n\n    public Drawable getSubscript() {\n        return subscript;\n    }\n\n    public int getImageRes() {\n        return imageRes;\n    }\n\n    public int getImageSkinSrcAttr() {\n        return imageSkinSrcAttr;\n    }\n\n    public int getImageSkinTintColorAttr() {\n        return imageSkinTintColorAttr;\n    }\n\n    public int getSubscriptRes() {\n        return subscriptRes;\n    }\n\n    public int getSubscriptSkinSrcAttr() {\n        return subscriptSkinSrcAttr;\n    }\n\n    public int getSubscriptSkinTintColorAttr() {\n        return subscriptSkinTintColorAttr;\n    }\n\n    public int getTextSkinColorAttr() {\n        return textSkinColorAttr;\n    }\n\n    public Object getTag() {\n        return tag;\n    }\n\n    public Typeface getTypeface() {\n        return typeface;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetGridItemView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.QMUISkinSimpleDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\n\npublic class QMUIBottomSheetGridItemView extends QMUIConstraintLayout {\n\n    protected AppCompatImageView mIconIv;\n    protected AppCompatImageView mSubscriptIv;\n    protected TextView mTitleTv;\n    protected Object mModelTag;\n\n\n    public QMUIBottomSheetGridItemView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIBottomSheetGridItemView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIBottomSheetGridItemView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setChangeAlphaWhenPress(true);\n        int paddingTop = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_grid_item_padding_top);\n        int paddingBottom = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_grid_item_padding_bottom);\n        setPadding(0, paddingTop, 0, paddingBottom);\n        mIconIv = onCreateIconView(context);\n        mIconIv.setId(View.generateViewId());\n        mIconIv.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n\n        int iconSize = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_grid_item_icon_size);\n        LayoutParams iconLp = new LayoutParams(iconSize, iconSize);\n        iconLp.leftToLeft = LayoutParams.PARENT_ID;\n        iconLp.rightToRight = LayoutParams.PARENT_ID;\n        iconLp.topToTop = LayoutParams.PARENT_ID;\n        addView(mIconIv, iconLp);\n\n        mTitleTv = onCreateTitleView(context);\n        mTitleTv.setId(View.generateViewId());\n        QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n        provider.setDefaultSkinAttr(QMUISkinValueBuilder.TEXT_COLOR,\n                R.attr.qmui_skin_support_bottom_sheet_grid_item_text_color);\n        QMUIResHelper.assignTextViewWithAttr(mTitleTv, R.attr.qmui_bottom_sheet_grid_item_text_style);\n        QMUISkinHelper.setSkinDefaultProvider(mTitleTv, provider);\n\n        LayoutParams titleLp = new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        titleLp.leftToLeft = LayoutParams.PARENT_ID;\n        titleLp.rightToRight = LayoutParams.PARENT_ID;\n        titleLp.topToBottom = mIconIv.getId();\n        titleLp.topMargin = QMUIResHelper.getAttrDimen(\n                context, R.attr.qmui_bottom_sheet_grid_item_text_margin_top);\n        addView(mTitleTv, titleLp);\n    }\n\n    protected AppCompatImageView onCreateIconView(Context context) {\n        return new AppCompatImageView(context);\n    }\n\n    protected TextView onCreateTitleView(Context context) {\n        return new QMUISpanTouchFixTextView(context);\n    }\n\n    public void render(@NonNull QMUIBottomSheetGridItemModel model) {\n        mModelTag = model.tag;\n        setTag(model.tag);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        renderIcon(model, builder);\n        builder.clear();\n        renderTitle(model, builder);\n        builder.clear();\n        renderSubScript(model, builder);\n        builder.release();\n    }\n\n    public Object getModelTag() {\n        return mModelTag;\n    }\n\n    protected void renderIcon(@NonNull QMUIBottomSheetGridItemModel model, @NonNull QMUISkinValueBuilder builder) {\n        if (model.imageSkinSrcAttr != 0) {\n            builder.src(model.imageSkinSrcAttr);\n            QMUISkinHelper.setSkinValue(mIconIv, builder);\n            Drawable drawable = QMUISkinHelper.getSkinDrawable(mIconIv, model.imageSkinSrcAttr);\n            mIconIv.setImageDrawable(drawable);\n        } else {\n            Drawable drawable = model.image;\n            if (drawable == null && model.imageRes != 0) {\n                drawable = ContextCompat.getDrawable(getContext(), model.imageRes);\n            }\n            if (drawable != null) {\n                drawable.mutate();\n            }\n            mIconIv.setImageDrawable(drawable);\n            if (model.imageSkinTintColorAttr != 0) {\n                builder.tintColor(model.imageSkinTintColorAttr);\n                QMUISkinHelper.setSkinValue(mIconIv, builder);\n            } else {\n                QMUISkinHelper.setSkinValue(mIconIv, \"\");\n            }\n        }\n    }\n\n    protected void renderTitle(@NonNull QMUIBottomSheetGridItemModel model, @NonNull QMUISkinValueBuilder builder) {\n        mTitleTv.setText(model.text);\n        if (model.textSkinColorAttr != 0) {\n            builder.textColor(model.textSkinColorAttr);\n        }\n        QMUISkinHelper.setSkinValue(mTitleTv, builder);\n        if (model.typeface != null) {\n            mTitleTv.setTypeface(model.typeface);\n        }\n    }\n\n    protected void renderSubScript(@NonNull QMUIBottomSheetGridItemModel model, @NonNull QMUISkinValueBuilder builder) {\n        if (model.subscriptRes != 0 || model.subscript != null || model.subscriptSkinSrcAttr != 0) {\n            if (mSubscriptIv == null) {\n                mSubscriptIv = new AppCompatImageView(getContext());\n                mSubscriptIv.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                LayoutParams lp = new LayoutParams(\n                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n                lp.rightToRight = mIconIv.getId();\n                lp.topToTop = mIconIv.getId();\n                addView(mSubscriptIv, lp);\n            }\n            mSubscriptIv.setVisibility(View.VISIBLE);\n            if (model.subscriptSkinSrcAttr != 0) {\n                builder.src(model.subscriptSkinSrcAttr);\n                QMUISkinHelper.setSkinValue(mSubscriptIv, builder);\n                Drawable drawable = QMUISkinHelper.getSkinDrawable(mSubscriptIv, model.subscriptSkinSrcAttr);\n                mIconIv.setImageDrawable(drawable);\n            } else {\n                Drawable drawable = model.subscript;\n                if (drawable == null && model.subscriptRes != 0) {\n                    drawable = ContextCompat.getDrawable(getContext(), model.subscriptRes);\n                }\n                if (drawable != null) {\n                    drawable.mutate();\n                }\n                mSubscriptIv.setImageDrawable(drawable);\n                if (model.subscriptSkinTintColorAttr != 0) {\n                    builder.tintColor(model.subscriptSkinTintColorAttr);\n                    QMUISkinHelper.setSkinValue(mSubscriptIv, builder);\n                } else {\n                    QMUISkinHelper.setSkinValue(mSubscriptIv, \"\");\n                }\n            }\n        } else if (mSubscriptIv != null) {\n            mSubscriptIv.setVisibility(View.GONE);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetGridLineLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.util.Pair;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.util.List;\n\n\npublic class QMUIBottomSheetGridLineLayout extends LinearLayout {\n\n    private static ItemWidthCalculator DEFAULT_CALCULATOR = new ItemWidthCalculator() {\n        @Override\n        public int calculate(Context context, int width, int miniWidth, int itemCount, int paddingLeft, int paddingRight) {\n            final int parentSpacing = width - paddingLeft - paddingRight;\n            int itemWidth = miniWidth;\n            // there is no more space for the last one item. then stretch the item width\n            if (itemCount >= 3\n                    && parentSpacing - itemCount * itemWidth > 0\n                    && parentSpacing - itemCount * itemWidth < itemWidth) {\n                int count = parentSpacing / itemWidth;\n                itemWidth = parentSpacing / count;\n            }\n            // if there are more items. then show half of the first that is exceeded\n            // to tell user that there are more.\n            if (itemWidth * itemCount > parentSpacing) {\n                int count = (width - paddingLeft) / itemWidth;\n                itemWidth = (int) ((width - paddingLeft) / (count + .5f));\n            }\n            return itemWidth;\n        }\n    };\n\n    private int maxItemCountInLines;\n    private int miniItemWidth = -1;\n    private List<Pair<View, LinearLayout.LayoutParams>> mFirstLineViews;\n    private List<Pair<View, LinearLayout.LayoutParams>> mSecondLineViews;\n    private int linePaddingHor;\n    private int itemWidth;\n    private final ItemWidthCalculator mItemWidthCalculator;\n    private final int mLineGravity;\n\n\n    public QMUIBottomSheetGridLineLayout(QMUIBottomSheet bottomSheet,\n                                         @Nullable ItemWidthCalculator widthCalculator,\n                                         int lineGravity,\n                                         List<Pair<View, LinearLayout.LayoutParams>> firstLineViews,\n                                         List<Pair<View, LinearLayout.LayoutParams>> secondLineViews) {\n        super(bottomSheet.getContext());\n        setOrientation(VERTICAL);\n        setGravity(Gravity.TOP);\n\n        mLineGravity = lineGravity;\n        mItemWidthCalculator = widthCalculator == null ? DEFAULT_CALCULATOR : widthCalculator;\n\n        int paddingTop = QMUIResHelper.getAttrDimen(\n                bottomSheet.getContext(), R.attr.qmui_bottom_sheet_grid_padding_top);\n        int paddingBottom = QMUIResHelper.getAttrDimen(\n                bottomSheet.getContext(), R.attr.qmui_bottom_sheet_grid_padding_bottom);\n        setPadding(0, paddingTop, 0, paddingBottom);\n        mFirstLineViews = firstLineViews;\n        mSecondLineViews = secondLineViews;\n        maxItemCountInLines = Math.max(\n                firstLineViews != null ? firstLineViews.size() : 0,\n                secondLineViews != null ? secondLineViews.size() : 0);\n        linePaddingHor = QMUIResHelper.getAttrDimen(\n                bottomSheet.getContext(), R.attr.qmui_bottom_sheet_padding_hor);\n\n        boolean hasFirstLine = false;\n        if (firstLineViews != null && !firstLineViews.isEmpty()) {\n            hasFirstLine = true;\n            HorizontalScrollView firstLine = createHorScroller(bottomSheet, firstLineViews);\n            addView(firstLine, new LinearLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        }\n\n        if (secondLineViews != null && !secondLineViews.isEmpty()) {\n            HorizontalScrollView secondLine = createHorScroller(bottomSheet, secondLineViews);\n            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            if (hasFirstLine) {\n                lp.topMargin = QMUIResHelper.getAttrDimen(\n                        bottomSheet.getContext(), R.attr.qmui_bottom_sheet_grid_line_vertical_space);\n            }\n            addView(secondLine, lp);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);\n        itemWidth = calculateItemWidth(\n                measureWidth, maxItemCountInLines, linePaddingHor, linePaddingHor);\n        if (mFirstLineViews != null) {\n            for (Pair<View, LinearLayout.LayoutParams> pair : mFirstLineViews) {\n                if (pair.second.width != itemWidth) {\n                    pair.second.width = itemWidth;\n                }\n            }\n        }\n\n        if (mSecondLineViews != null) {\n            for (Pair<View, LinearLayout.LayoutParams> pair : mSecondLineViews) {\n                if (pair.second.width != itemWidth) {\n                    pair.second.width = itemWidth;\n                }\n            }\n        }\n\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n    protected HorizontalScrollView createHorScroller(\n            QMUIBottomSheet bottomSheet,\n            List<Pair<View, LinearLayout.LayoutParams>> itemViews) {\n        Context context = bottomSheet.getContext();\n        HorizontalScrollView scroller = new HorizontalScrollView(context);\n        scroller.setHorizontalScrollBarEnabled(false);\n        scroller.setClipToPadding(true);\n\n        LinearLayout linear = new LinearLayout(context);\n        linear.setOrientation(LinearLayout.HORIZONTAL);\n        linear.setGravity(mLineGravity);\n        linear.setPadding(linePaddingHor, 0, linePaddingHor, 0);\n        scroller.addView(linear, new HorizontalScrollView.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n\n        for (int i = 0; i < itemViews.size(); i++) {\n            Pair<View, LinearLayout.LayoutParams> pair = itemViews.get(i);\n            linear.addView(pair.first, pair.second);\n        }\n\n        return scroller;\n    }\n\n\n    @Override\n    protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {\n        super.measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec);\n    }\n\n    private int calculateItemWidth(int width, int calculateCount, int paddingLeft, int paddingRight) {\n        if (miniItemWidth == -1) {\n            miniItemWidth = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_bottom_sheet_grid_item_mini_width);\n        }\n        return mItemWidthCalculator.calculate(getContext(), width, miniItemWidth, calculateCount, paddingLeft, paddingRight);\n    }\n\n    public interface ItemWidthCalculator {\n        int calculate(Context context, int width, int miniWidth, int itemCount, int paddingLeft, int paddingRight);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetListAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUIBottomSheetListAdapter extends RecyclerView.Adapter<QMUIBottomSheetListAdapter.VH> {\n\n    public static final int ITEM_TYPE_HEADER = 1;\n    public static final int ITEM_TYPE_FOOTER = 2;\n    public static final int ITEM_TYPE_NORMAL = 3;\n\n    @Nullable\n    private View mHeaderView;\n    @Nullable\n    private View mFooterView;\n    private List<QMUIBottomSheetListItemModel> mData = new ArrayList<>();\n    private final boolean mNeedMark;\n    private final boolean mGravityCenter;\n    private int mCheckedIndex = -1;\n    private OnItemClickListener mOnItemClickListener;\n\n    public QMUIBottomSheetListAdapter(boolean needMark, boolean gravityCenter){\n        mNeedMark = needMark;\n        mGravityCenter = gravityCenter;\n    }\n\n    public void setCheckedIndex(int checkedIndex) {\n        mCheckedIndex = checkedIndex;\n        notifyDataSetChanged();\n    }\n\n    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {\n        mOnItemClickListener = onItemClickListener;\n    }\n\n    public void setData(@Nullable View headerView,\n                        @Nullable View footerView,\n                        List<QMUIBottomSheetListItemModel> data) {\n        mHeaderView = headerView;\n        mFooterView = footerView;\n        mData.clear();\n        if (data != null) {\n            mData.addAll(data);\n        }\n        notifyDataSetChanged();\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        if(mHeaderView != null){\n            if(position == 0){\n                return ITEM_TYPE_HEADER;\n            }\n        }\n        if(position == getItemCount() - 1){\n            if(mFooterView != null){\n                return ITEM_TYPE_FOOTER;\n            }\n        }\n        return ITEM_TYPE_NORMAL;\n    }\n\n    @NonNull\n    @Override\n    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        if(viewType == ITEM_TYPE_HEADER){\n            return new VH(mHeaderView);\n        }else if(viewType == ITEM_TYPE_FOOTER){\n            return new VH(mFooterView);\n        }\n        final VH vh = new VH(new QMUIBottomSheetListItemView(\n                parent.getContext(), mNeedMark, mGravityCenter));\n        vh.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if(mOnItemClickListener != null){\n                    int adapterPosition = vh.getAdapterPosition();\n                    int dataPos = mHeaderView != null ? adapterPosition - 1 : adapterPosition;\n                    mOnItemClickListener.onClick(vh, dataPos, mData.get(dataPos));\n                }\n            }\n        });\n        return vh;\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull VH holder, int position) {\n        if(holder.getItemViewType() != ITEM_TYPE_NORMAL){\n            return;\n        }\n        if(mHeaderView != null){\n            position--;\n        }\n        QMUIBottomSheetListItemModel itemModel = mData.get(position);\n        QMUIBottomSheetListItemView itemView = (QMUIBottomSheetListItemView) holder.itemView;\n        itemView.render(itemModel, position == mCheckedIndex);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mData.size() + (mHeaderView != null ? 1 : 0) + (mFooterView != null ? 1 : 0);\n    }\n\n    public static class VH extends RecyclerView.ViewHolder {\n\n        public VH(@NonNull View itemView) {\n            super(itemView);\n        }\n    }\n\n    public interface OnItemClickListener {\n        void onClick(VH vh, int dataPos, QMUIBottomSheetListItemModel model);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetListItemDecoration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerDecoration;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUIBottomSheetListItemDecoration extends RecyclerView.ItemDecoration\n        implements IQMUISkinHandlerDecoration {\n\n    private final Paint mSeparatorPaint;\n    private final int mSeparatorAttr;\n\n    public QMUIBottomSheetListItemDecoration(Context context) {\n        mSeparatorPaint = new Paint();\n        mSeparatorPaint.setStrokeWidth(\n                QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_list_item_separator_height));\n        mSeparatorPaint.setStyle(Paint.Style.STROKE);\n        mSeparatorAttr = R.attr.qmui_skin_support_bottom_sheet_separator_color;\n        if (mSeparatorAttr != 0) {\n            mSeparatorPaint.setColor(QMUIResHelper.getAttrColor(context, mSeparatorAttr));\n        }\n    }\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        super.onDrawOver(c, parent, state);\n        RecyclerView.Adapter adapter = parent.getAdapter();\n        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();\n        if (adapter == null || layoutManager == null || mSeparatorAttr == 0) {\n            return;\n        }\n        for (int i = 0; i < parent.getChildCount(); i++) {\n            View view = parent.getChildAt(i);\n            int position = parent.getChildAdapterPosition(view);\n            if (view instanceof QMUIBottomSheetListItemView) {\n                if (position > 0 &&\n                        adapter.getItemViewType(position - 1) != QMUIBottomSheetListAdapter.ITEM_TYPE_NORMAL) {\n                    int top = layoutManager.getDecoratedTop(view);\n                    c.drawLine(0, top, parent.getWidth(), top, mSeparatorPaint);\n                }\n                if (position + 1 < adapter.getItemCount() &&\n                        adapter.getItemViewType(position + 1) == QMUIBottomSheetListAdapter.ITEM_TYPE_NORMAL) {\n                    int bottom = layoutManager.getDecoratedBottom(view);\n                    c.drawLine(0, bottom, parent.getWidth(), bottom, mSeparatorPaint);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void handle(@NotNull RecyclerView recyclerView,\n                       @NotNull QMUISkinManager manager,\n                       int skinIndex,\n                       @NotNull Resources.Theme theme) {\n        if (mSeparatorAttr != 0) {\n            mSeparatorPaint.setColor(QMUIResHelper.getAttrColor(theme, mSeparatorAttr));\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetListItemModel.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\n\npublic class QMUIBottomSheetListItemModel {\n    Drawable image = null;\n    int imageRes = 0;\n    int imageSkinTintColorAttr = 0;\n    int imageSkinSrcAttr = 0;\n    int textSkinColorAttr = 0;\n    CharSequence text;\n    String tag = \"\";\n    boolean hasRedPoint = false;\n    boolean isDisabled = false;\n    Typeface typeface;\n\n    public QMUIBottomSheetListItemModel(CharSequence text, String tag) {\n        this.text = text;\n        this.tag = tag;\n    }\n\n    public QMUIBottomSheetListItemModel image(Drawable image) {\n        this.image = image;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel image(int imageRes) {\n        this.imageRes = imageRes;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel skinTextColorAttr(int attr) {\n        this.textSkinColorAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel skinImageTintColorAttr(int attr) {\n        this.imageSkinTintColorAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel skinImageSrcAttr(int attr) {\n        this.imageSkinSrcAttr = attr;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel redPoint(boolean hasRedPoint) {\n        this.hasRedPoint = hasRedPoint;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel disabled(boolean isDisabled) {\n        this.isDisabled = isDisabled;\n        return this;\n    }\n\n    public QMUIBottomSheetListItemModel typeface(Typeface typeface){\n        this.typeface = typeface;\n        return this;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetListItemView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.QMUISkinSimpleDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\npublic class QMUIBottomSheetListItemView extends QMUIConstraintLayout {\n\n    private AppCompatImageView mIconView;\n    private QMUISpanTouchFixTextView mTextView;\n    private QMUIFrameLayout mRedPointView;\n    private AppCompatImageView mMarkView = null;\n    private int mItemHeight;\n\n    public QMUIBottomSheetListItemView(Context context, boolean markStyle, boolean gravityCenter) {\n        super(context);\n        setBackground(QMUIResHelper.getAttrDrawable(\n                context, R.attr.qmui_skin_support_bottom_sheet_list_item_bg));\n        int paddingHor = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_padding_hor);\n        setPadding(paddingHor, 0, paddingHor, 0);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.background(R.attr.qmui_skin_support_bottom_sheet_list_item_bg);\n        QMUISkinHelper.setSkinValue(this, builder);\n        builder.clear();\n\n        mIconView = new AppCompatImageView(context);\n        mIconView.setId(View.generateViewId());\n        mIconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n\n        mTextView = new QMUISpanTouchFixTextView(context);\n        mTextView.setId(View.generateViewId());\n        QMUISkinSimpleDefaultAttrProvider provider = new QMUISkinSimpleDefaultAttrProvider();\n        provider.setDefaultSkinAttr(QMUISkinValueBuilder.TEXT_COLOR,\n                R.attr.qmui_skin_support_bottom_sheet_list_item_text_color);\n        QMUIResHelper.assignTextViewWithAttr(mTextView, R.attr.qmui_bottom_sheet_list_item_text_style);\n        QMUISkinHelper.setSkinDefaultProvider(mTextView, provider);\n\n        mRedPointView = new QMUIFrameLayout(context);\n        mRedPointView.setId(View.generateViewId());\n        mRedPointView.setBackgroundColor(QMUIResHelper.getAttrColor(\n                context, R.attr.qmui_skin_support_bottom_sheet_list_red_point_color));\n        builder.background(R.attr.qmui_skin_support_bottom_sheet_list_red_point_color);\n        QMUISkinHelper.setSkinValue(mRedPointView, builder);\n        builder.clear();\n\n        if (markStyle) {\n            mMarkView = new AppCompatImageView(context);\n            mMarkView.setId(View.generateViewId());\n            mMarkView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n            mMarkView.setImageDrawable(QMUIResHelper.getAttrDrawable(\n                    context, R.attr.qmui_skin_support_bottom_sheet_list_mark));\n            builder.src(R.attr.qmui_skin_support_bottom_sheet_list_mark);\n            QMUISkinHelper.setSkinValue(mMarkView, builder);\n        }\n        builder.release();\n\n        int iconSize = QMUIResHelper.getAttrDimen(\n                context, R.attr.qmui_bottom_sheet_list_item_icon_size);\n        LayoutParams lp = new ConstraintLayout.LayoutParams(iconSize, iconSize);\n        lp.leftToLeft = LayoutParams.PARENT_ID;\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.rightToLeft = mTextView.getId();\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        lp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        lp.horizontalBias = gravityCenter ? 0.5f : 0f;\n        addView(mIconView, lp);\n\n        lp = new ConstraintLayout.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToRight = mIconView.getId();\n        lp.rightToLeft = mRedPointView.getId();\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        lp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        lp.horizontalBias = gravityCenter ? 0.5f : 0f;\n        lp.leftMargin = QMUIResHelper.getAttrDimen(\n                context, R.attr.qmui_bottom_sheet_list_item_icon_margin_right);\n        lp.goneLeftMargin = 0;\n        addView(mTextView, lp);\n\n        int redPointSize = QMUIResHelper.getAttrDimen(\n                context, R.attr.qmui_bottom_sheet_list_item_red_point_size);\n        lp = new ConstraintLayout.LayoutParams(redPointSize, redPointSize);\n        lp.leftToRight = mTextView.getId();\n        if (markStyle) {\n            lp.rightToLeft = mMarkView.getId();\n            lp.rightMargin = QMUIResHelper.getAttrDimen(\n                    context, R.attr.qmui_bottom_sheet_list_item_mark_margin_left);\n        } else {\n            lp.rightToRight = LayoutParams.PARENT_ID;\n        }\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        lp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        lp.horizontalBias = gravityCenter ? 0.5f : 0f;\n        lp.leftMargin = QMUIResHelper.getAttrDimen(\n                context, R.attr.qmui_bottom_sheet_list_item_tip_point_margin_left);\n        addView(mRedPointView, lp);\n\n        if (markStyle) {\n            lp = new ConstraintLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            lp.rightToRight = LayoutParams.PARENT_ID;\n            lp.topToTop = LayoutParams.PARENT_ID;\n            lp.bottomToBottom = LayoutParams.PARENT_ID;\n            addView(mMarkView, lp);\n        }\n\n        mItemHeight = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_list_item_height);\n    }\n\n    public void render(@NonNull QMUIBottomSheetListItemModel itemModel, boolean isChecked) {\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        if (itemModel.imageSkinSrcAttr != 0) {\n            builder.src(itemModel.imageSkinSrcAttr);\n            QMUISkinHelper.setSkinValue(mIconView, builder);\n            mIconView.setImageDrawable(\n                    QMUISkinHelper.getSkinDrawable(this, itemModel.imageSkinSrcAttr));\n            mIconView.setVisibility(View.VISIBLE);\n        } else {\n            Drawable drawable = itemModel.image;\n            if (drawable == null && itemModel.imageRes != 0) {\n                drawable = ContextCompat.getDrawable(getContext(), itemModel.imageRes);\n            }\n            if (drawable != null) {\n                drawable.mutate();\n                mIconView.setImageDrawable(drawable);\n                if (itemModel.imageSkinTintColorAttr != 0) {\n                    builder.tintColor(itemModel.imageSkinTintColorAttr);\n                    QMUISkinHelper.setSkinValue(mIconView, builder);\n                } else {\n                    QMUISkinHelper.setSkinValue(mIconView, \"\");\n                }\n            } else {\n                mIconView.setVisibility(View.GONE);\n            }\n        }\n        builder.clear();\n\n        mTextView.setText(itemModel.text);\n        if (itemModel.typeface != null) {\n            mTextView.setTypeface(itemModel.typeface);\n        }\n        if (itemModel.textSkinColorAttr != 0) {\n            builder.textColor(itemModel.textSkinColorAttr);\n            QMUISkinHelper.setSkinValue(mTextView, builder);\n            ColorStateList color = QMUISkinHelper.getSkinColorStateList(mTextView, itemModel.textSkinColorAttr);\n            if (color != null) {\n                mTextView.setTextColor(color);\n            }\n        } else {\n            QMUISkinHelper.setSkinValue(mTextView, \"\");\n        }\n\n        mRedPointView.setVisibility(itemModel.hasRedPoint ? View.VISIBLE : View.GONE);\n\n        if (mMarkView != null) {\n            mMarkView.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY));\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIBottomSheetRootLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\npublic class QMUIBottomSheetRootLayout extends QMUIPriorityLinearLayout {\n\n    private final int mUsePercentMinHeight;\n    private final float mHeightPercent;\n    private final int mMaxWidth;\n\n    public QMUIBottomSheetRootLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIBottomSheetRootLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setOrientation(VERTICAL);\n        setBackground(QMUIResHelper.getAttrDrawable(context, R.attr.qmui_skin_support_bottom_sheet_bg));\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.background(R.attr.qmui_skin_support_bottom_sheet_bg);\n        QMUISkinHelper.setSkinValue(this, builder);\n        builder.release();\n\n        int radius = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_radius);\n        if (radius > 0) {\n            setRadius(radius, HIDE_RADIUS_SIDE_BOTTOM);\n        }\n        mUsePercentMinHeight = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_use_percent_min_height);\n        mHeightPercent = QMUIResHelper.getAttrFloatValue(context, R.attr.qmui_bottom_sheet_height_percent);\n        mMaxWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_bottom_sheet_max_width);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        if (widthSize > mMaxWidth) {\n            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, widthMode);\n        }\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        if (heightSize >= mUsePercentMinHeight) {\n            heightMeasureSpec = MeasureSpec.makeMeasureSpec(\n                    (int) (heightSize * mHeightPercent), MeasureSpec.AT_MOST);\n        }\n\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialog.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.text.InputType;\nimport android.text.TextWatcher;\nimport android.text.method.TransformationMethod;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.view.inputmethod.EditorInfo;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.ScrollView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.layout.QMUILinearLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatEditText;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\n/**\n * QMUIDialog 对话框一般由 {@link QMUIDialogBuilder} 及其子类创建, 不同的 Builder 可以创建不同类型的对话框,\n * 例如消息类型的对话框、菜单项对话框等等。\n *\n * @author cginechen\n * @date 2015-10-20\n * @see QMUIDialogBuilder\n */\npublic class QMUIDialog extends QMUIBaseDialog {\n    private Context mBaseContext;\n\n    public QMUIDialog(Context context) {\n        this(context, R.style.QMUI_Dialog);\n    }\n\n    public QMUIDialog(Context context, int styleRes) {\n        super(context, styleRes);\n        mBaseContext = context;\n        init();\n    }\n\n    private void init() {\n        setCancelable(true);\n        setCanceledOnTouchOutside(true);\n    }\n\n\n    public void showWithImmersiveCheck(Activity activity) {\n        // http://stackoverflow.com/questions/22794049/how-to-maintain-the-immersive-mode-in-dialogs\n        Window window = getWindow();\n        if (window == null) {\n            return;\n        }\n\n        Window activityWindow = activity.getWindow();\n        int activitySystemUi = activityWindow.getDecorView().getSystemUiVisibility();\n        if ((activitySystemUi & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ||\n                (activitySystemUi & View.SYSTEM_UI_FLAG_FULLSCREEN) == View.SYSTEM_UI_FLAG_FULLSCREEN) {\n            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,\n                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);\n            window.getDecorView().setSystemUiVisibility(\n                    activity.getWindow().getDecorView().getSystemUiVisibility());\n            super.show();\n            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);\n        } else {\n            super.show();\n        }\n    }\n\n    public void showWithImmersiveCheck() {\n        if (!(mBaseContext instanceof Activity)) {\n            super.show();\n            return;\n        }\n        Activity activity = (Activity) mBaseContext;\n        showWithImmersiveCheck(activity);\n    }\n\n\n    /**\n     * 消息类型的对话框 Builder。通过它可以生成一个带标题、文本消息、按钮的对话框。\n     */\n    public static class MessageDialogBuilder extends QMUIDialogBuilder<MessageDialogBuilder> {\n        protected CharSequence mMessage;\n\n        public MessageDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 设置对话框的消息文本\n         */\n        public MessageDialogBuilder setMessage(CharSequence message) {\n            this.mMessage = message;\n            return this;\n        }\n\n        /**\n         * 设置对话框的消息文本\n         */\n        public MessageDialogBuilder setMessage(int resId) {\n            return setMessage(getBaseContext().getResources().getString(resId));\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {\n            if (mMessage != null && mMessage.length() != 0) {\n                QMUISpanTouchFixTextView tv = new QMUISpanTouchFixTextView(context);\n                assignMessageTvWithAttr(tv, hasTitle(), R.attr.qmui_dialog_message_content_style);\n                tv.setText(mMessage);\n                tv.setMovementMethodDefault();\n\n                QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n                valueBuilder.textColor(R.attr.qmui_skin_support_dialog_message_text_color);\n                QMUISkinHelper.setSkinValue(tv, valueBuilder);\n                QMUISkinValueBuilder.release(valueBuilder);\n\n                return wrapWithScroll(tv);\n            }\n            return null;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateTitle(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {\n            View tv = super.onCreateTitle(dialog, parent, context);\n            if (tv != null && (mMessage == null || mMessage.length() == 0)) {\n                TypedArray a = context.obtainStyledAttributes(null,\n                        R.styleable.QMUIDialogTitleTvCustomDef, R.attr.qmui_dialog_title_style, 0);\n                int count = a.getIndexCount();\n                for (int i = 0; i < count; i++) {\n                    int attr = a.getIndex(i);\n                    if (attr == R.styleable.QMUIDialogTitleTvCustomDef_qmui_paddingBottomWhenNotContent) {\n                        tv.setPadding(\n                                tv.getPaddingLeft(),\n                                tv.getPaddingTop(),\n                                tv.getPaddingRight(),\n                                a.getDimensionPixelSize(attr, tv.getPaddingBottom())\n                        );\n                    }\n                }\n                a.recycle();\n            }\n            return tv;\n        }\n\n        public static void assignMessageTvWithAttr(TextView messageTv, boolean hasTitle, int defAttr) {\n            QMUIResHelper.assignTextViewWithAttr(messageTv, defAttr);\n\n            if (!hasTitle) {\n                TypedArray a = messageTv.getContext().obtainStyledAttributes(null,\n                        R.styleable.QMUIDialogMessageTvCustomDef, defAttr, 0);\n                int count = a.getIndexCount();\n                for (int i = 0; i < count; i++) {\n                    int attr = a.getIndex(i);\n                    if (attr == R.styleable.QMUIDialogMessageTvCustomDef_qmui_paddingTopWhenNotTitle) {\n                        messageTv.setPadding(\n                                messageTv.getPaddingLeft(),\n                                a.getDimensionPixelSize(attr, messageTv.getPaddingTop()),\n                                messageTv.getPaddingRight(),\n                                messageTv.getPaddingBottom()\n                        );\n                    }\n                }\n                a.recycle();\n            }\n        }\n    }\n\n    /**\n     * 带 CheckBox 的消息确认框 Builder\n     */\n    public static class CheckBoxMessageDialogBuilder extends QMUIDialogBuilder<CheckBoxMessageDialogBuilder> {\n        protected String mMessage;\n        private boolean mIsChecked = false;\n        private QMUISpanTouchFixTextView mTextView;\n\n        public CheckBoxMessageDialogBuilder(Context context) {\n            super(context);\n\n        }\n\n        /**\n         * 设置对话框的消息文本\n         */\n        public CheckBoxMessageDialogBuilder setMessage(String message) {\n            this.mMessage = message;\n            return this;\n        }\n\n        /**\n         * 设置对话框的消息文本\n         */\n        public CheckBoxMessageDialogBuilder setMessage(int resid) {\n            return setMessage(getBaseContext().getResources().getString(resid));\n        }\n\n        /**\n         * CheckBox 是否处于勾选状态\n         */\n        public boolean isChecked() {\n            return mIsChecked;\n        }\n\n        /**\n         * 设置 CheckBox 的勾选状态\n         */\n        public CheckBoxMessageDialogBuilder setChecked(boolean checked) {\n            if (mIsChecked != checked) {\n                mIsChecked = checked;\n                if (mTextView != null) {\n                    mTextView.setSelected(checked);\n                }\n            }\n\n            return this;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            if (mMessage != null && mMessage.length() != 0) {\n                mTextView = new QMUISpanTouchFixTextView(context);\n                mTextView.setMovementMethodDefault();\n                MessageDialogBuilder.assignMessageTvWithAttr(mTextView, hasTitle(), R.attr.qmui_dialog_message_content_style);\n                mTextView.setText(mMessage);\n                Drawable drawable = QMUISkinHelper.getSkinDrawable(mTextView, R.attr.qmui_skin_support_s_dialog_check_drawable);\n                if (drawable != null) {\n                    drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n                    mTextView.setCompoundDrawables(drawable, null, null, null);\n                }\n                QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n                valueBuilder.textColor(R.attr.qmui_skin_support_dialog_message_text_color);\n                valueBuilder.textCompoundLeftSrc(R.attr.qmui_skin_support_s_dialog_check_drawable);\n                QMUISkinHelper.setSkinValue(mTextView, valueBuilder);\n                QMUISkinValueBuilder.release(valueBuilder);\n                mTextView.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        setChecked(!mIsChecked);\n                    }\n                });\n                mTextView.setSelected(mIsChecked);\n                return wrapWithScroll(mTextView);\n            }\n            return null;\n        }\n\n        @Deprecated\n        public QMUISpanTouchFixTextView getTextView() {\n            return mTextView;\n        }\n\n    }\n\n    /**\n     * 带输入框的对话框 Builder\n     */\n    public static class EditTextDialogBuilder extends QMUIDialogBuilder<EditTextDialogBuilder> {\n        protected String mPlaceholder;\n        protected TransformationMethod mTransformationMethod;\n        protected EditText mEditText;\n        protected AppCompatImageView mRightImageView;\n        private int mInputType = InputType.TYPE_CLASS_TEXT;\n        private CharSequence mDefaultText = null;\n        private TextWatcher mTextWatcher;\n\n        public EditTextDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 设置输入框的 placeholder\n         */\n        public EditTextDialogBuilder setPlaceholder(String placeholder) {\n            this.mPlaceholder = placeholder;\n            return this;\n        }\n\n        /**\n         * 设置输入框的 placeholder\n         */\n        public EditTextDialogBuilder setPlaceholder(int resId) {\n            return setPlaceholder(getBaseContext().getResources().getString(resId));\n        }\n\n        public EditTextDialogBuilder setDefaultText(CharSequence defaultText) {\n            mDefaultText = defaultText;\n            return this;\n        }\n\n        /**\n         * 设置 EditText 的 transformationMethod\n         */\n        public EditTextDialogBuilder setTransformationMethod(TransformationMethod transformationMethod) {\n            mTransformationMethod = transformationMethod;\n            return this;\n        }\n\n        /**\n         * 设置 EditText 的 inputType\n         */\n        public EditTextDialogBuilder setInputType(int inputType) {\n            mInputType = inputType;\n            return this;\n        }\n\n        public EditTextDialogBuilder setTextWatcher(TextWatcher textWatcher) {\n            mTextWatcher = textWatcher;\n            return this;\n        }\n\n        @Override\n        protected ConstraintLayout.LayoutParams onCreateContentLayoutParams(Context context) {\n            ConstraintLayout.LayoutParams lp = super.onCreateContentLayoutParams(context);\n            int marginHor = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_padding_horizontal);\n            lp.leftMargin = marginHor;\n            lp.rightMargin = marginHor;\n            lp.topMargin = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_edit_margin_top);\n            lp.bottomMargin = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_edit_margin_bottom);\n            return lp;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            QMUIConstraintLayout boxLayout = new QMUIConstraintLayout(context);\n            boxLayout.onlyShowBottomDivider(0, 0,\n                    QMUIResHelper.getAttrDimen(context,\n                            R.attr.qmui_dialog_edit_bottom_line_height),\n                    QMUIResHelper.getAttrColor(context,\n                            R.attr.qmui_skin_support_dialog_edit_bottom_line_color));\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            builder.bottomSeparator(R.attr.qmui_skin_support_dialog_edit_bottom_line_color);\n            QMUISkinHelper.setSkinValue(boxLayout, builder);\n\n            mEditText = new AppCompatEditText(context);\n            mEditText.setBackgroundResource(0);\n            MessageDialogBuilder.assignMessageTvWithAttr(mEditText, hasTitle(), R.attr.qmui_dialog_edit_content_style);\n            mEditText.setFocusable(true);\n            mEditText.setFocusableInTouchMode(true);\n            mEditText.setImeOptions(EditorInfo.IME_ACTION_GO);\n            mEditText.setId(R.id.qmui_dialog_edit_input);\n\n            if (!QMUILangHelper.isNullOrEmpty(mDefaultText)) {\n                mEditText.setText(mDefaultText);\n            }\n            if (mTextWatcher != null) {\n                mEditText.addTextChangedListener(mTextWatcher);\n            }\n            builder.clear();\n            builder.textColor(R.attr.qmui_skin_support_dialog_edit_text_color);\n            builder.hintColor(R.attr.qmui_skin_support_dialog_edit_text_hint_color);\n            QMUISkinHelper.setSkinValue(mEditText, builder);\n            QMUISkinValueBuilder.release(builder);\n\n\n            mRightImageView = new AppCompatImageView(context);\n            mRightImageView.setId(R.id.qmui_dialog_edit_right_icon);\n            mRightImageView.setVisibility(View.GONE);\n            configRightImageView(mRightImageView, mEditText);\n\n            if (mTransformationMethod != null) {\n                mEditText.setTransformationMethod(mTransformationMethod);\n            } else {\n                mEditText.setInputType(mInputType);\n            }\n\n            if (mPlaceholder != null) {\n                mEditText.setHint(mPlaceholder);\n            }\n            boxLayout.addView(mEditText, createEditTextLayoutParams(context));\n            boxLayout.addView(mRightImageView, createRightIconLayoutParams(context));\n\n            return boxLayout;\n        }\n\n        protected void configRightImageView(AppCompatImageView imageView, EditText editText) {\n\n        }\n\n        protected ConstraintLayout.LayoutParams createEditTextLayoutParams(Context context) {\n            ConstraintLayout.LayoutParams editLp = new ConstraintLayout.LayoutParams(\n                    0, ViewGroup.LayoutParams.WRAP_CONTENT);\n            editLp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n            editLp.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n            editLp.rightToLeft = R.id.qmui_dialog_edit_right_icon;\n            editLp.rightToRight = QMUIDisplayHelper.dp2px(context, 5);\n            editLp.goneRightMargin = 0;\n            return editLp;\n        }\n\n        protected ConstraintLayout.LayoutParams createRightIconLayoutParams(Context context) {\n            ConstraintLayout.LayoutParams rightIconLp = new ConstraintLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            rightIconLp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n            rightIconLp.bottomToBottom = R.id.qmui_dialog_edit_input;\n            return rightIconLp;\n        }\n\n        @Override\n        protected void onAfterCreate(QMUIDialog dialog, QMUIDialogRootLayout rootLayout, Context context) {\n            super.onAfterCreate(dialog, rootLayout, context);\n            final InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n            dialog.setOnDismissListener(new OnDismissListener() {\n                @Override\n                public void onDismiss(DialogInterface dialog) {\n                    inputMethodManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);\n                }\n            });\n            mEditText.postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    mEditText.requestFocus();\n                    inputMethodManager.showSoftInput(mEditText, 0);\n                }\n            }, 300);\n        }\n\n        /**\n         * 注意该方法只在调用 {@link #create()} 或 {@link #create(int)} 或 {@link #show()} 生成 Dialog 之后\n         * 才能返回对应的 EditText，在此之前将返回 null\n         */\n        @Deprecated\n        public EditText getEditText() {\n            return mEditText;\n        }\n\n        public ImageView getRightImageView() {\n            return mRightImageView;\n        }\n    }\n\n\n    public static class MenuBaseDialogBuilder<T extends QMUIDialogBuilder> extends QMUIDialogBuilder<T> {\n        protected ArrayList<ItemViewFactory> mMenuItemViewsFactoryList;\n        protected ArrayList<QMUIDialogMenuItemView> mMenuItemViews = new ArrayList<>();\n\n        public MenuBaseDialogBuilder(Context context) {\n            super(context);\n            mMenuItemViewsFactoryList = new ArrayList<>();\n        }\n\n        public void clear() {\n            mMenuItemViewsFactoryList.clear();\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        @Deprecated\n        public T addItem(final QMUIDialogMenuItemView itemView, final OnClickListener listener) {\n            itemView.setMenuIndex(mMenuItemViewsFactoryList.size());\n            itemView.setListener(new QMUIDialogMenuItemView.MenuItemViewListener() {\n                @Override\n                public void onClick(int index) {\n                    onItemClick(index);\n                    if (listener != null) {\n                        listener.onClick(mDialog, index);\n                    }\n                }\n            });\n            mMenuItemViewsFactoryList.add(new ItemViewFactory() {\n                @Override\n                public QMUIDialogMenuItemView createItemView(Context context) {\n                    return itemView;\n                }\n            });\n            return (T) this;\n        }\n\n        public T addItem(final ItemViewFactory itemViewFactory, final OnClickListener listener) {\n            mMenuItemViewsFactoryList.add(new ItemViewFactory() {\n                @Override\n                public QMUIDialogMenuItemView createItemView(Context context) {\n                    QMUIDialogMenuItemView itemView = itemViewFactory.createItemView(context);\n                    itemView.setMenuIndex(mMenuItemViewsFactoryList.indexOf(this));\n                    itemView.setListener(new QMUIDialogMenuItemView.MenuItemViewListener() {\n                        @Override\n                        public void onClick(int index) {\n                            onItemClick(index);\n                            if (listener != null) {\n                                listener.onClick(mDialog, index);\n                            }\n                        }\n                    });\n                    return itemView;\n                }\n            });\n            return (T) this;\n        }\n\n        protected void onItemClick(int index) {\n\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            LinearLayout layout = new QMUILinearLayout(context);\n            layout.setOrientation(LinearLayout.VERTICAL);\n\n\n            TypedArray a = context.obtainStyledAttributes(\n                    null, R.styleable.QMUIDialogMenuContainerStyleDef, R.attr.qmui_dialog_menu_container_style, 0);\n            int count = a.getIndexCount();\n            int paddingTop = 0, paddingBottom = 0, paddingVerWhenSingle = 0,\n                    paddingTopWhenTitle = 0, paddingBottomWhenAction = 0, itemHeight = -1;\n            for (int i = 0; i < count; i++) {\n                int attr = a.getIndex(i);\n                if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_android_paddingTop) {\n                    paddingTop = a.getDimensionPixelSize(attr, paddingTop);\n                } else if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_android_paddingBottom) {\n                    paddingBottom = a.getDimensionPixelSize(attr, paddingBottom);\n                } else if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_qmui_dialog_menu_container_single_padding_vertical) {\n                    paddingVerWhenSingle = a.getDimensionPixelSize(attr, paddingVerWhenSingle);\n                } else if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_qmui_dialog_menu_container_padding_top_when_title_exist) {\n                    paddingTopWhenTitle = a.getDimensionPixelSize(attr, paddingTopWhenTitle);\n                } else if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_qmui_dialog_menu_container_padding_bottom_when_action_exist) {\n                    paddingBottomWhenAction = a.getDimensionPixelSize(attr, paddingBottomWhenAction);\n                } else if (attr == R.styleable.QMUIDialogMenuContainerStyleDef_qmui_dialog_menu_item_height) {\n                    itemHeight = a.getDimensionPixelSize(attr, itemHeight);\n                }\n            }\n            a.recycle();\n\n            if (mMenuItemViewsFactoryList.size() == 1) {\n                paddingBottom = paddingTop = paddingVerWhenSingle;\n            }\n\n            if (hasTitle()) {\n                paddingTop = paddingTopWhenTitle;\n            }\n\n            if (mActions.size() > 0) {\n                paddingBottom = paddingBottomWhenAction;\n            }\n\n            layout.setPadding(0, paddingTop, 0, paddingBottom);\n\n            LinearLayout.LayoutParams itemLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, itemHeight);\n            itemLp.gravity = Gravity.CENTER_VERTICAL;\n\n\n            mMenuItemViews.clear();\n            for (ItemViewFactory factory : mMenuItemViewsFactoryList) {\n                QMUIDialogMenuItemView itemView = factory.createItemView(context);\n                layout.addView(itemView, itemLp);\n                mMenuItemViews.add(itemView);\n            }\n            return wrapWithScroll(layout);\n        }\n\n        public interface ItemViewFactory {\n            QMUIDialogMenuItemView createItemView(Context context);\n        }\n    }\n\n    /**\n     * 菜单类型的对话框 Builder\n     */\n    public static class MenuDialogBuilder extends MenuBaseDialogBuilder<MenuDialogBuilder> {\n\n        public MenuDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 添加多个菜单项\n         *\n         * @param items    所有菜单项的文字\n         * @param listener 菜单项的点击事件\n         */\n        public MenuDialogBuilder addItems(CharSequence[] items, OnClickListener listener) {\n            for (final CharSequence item : items) {\n                addItem(item, listener);\n            }\n            return this;\n        }\n\n        /**\n         * 添加单个菜单项\n         *\n         * @param item     菜单项的文字\n         * @param listener 菜单项的点击事件\n         */\n        public MenuDialogBuilder addItem(final CharSequence item, OnClickListener listener) {\n            addItem(new ItemViewFactory() {\n                @Override\n                public QMUIDialogMenuItemView createItemView(Context context) {\n                    return new QMUIDialogMenuItemView.TextItemView(context, item);\n                }\n            }, listener);\n            return this;\n        }\n\n    }\n\n    /**\n     * 单选类型的对话框 Builder\n     */\n    public static class CheckableDialogBuilder extends MenuBaseDialogBuilder<CheckableDialogBuilder> {\n\n        /**\n         * 当前被选中的菜单项的下标, 负数表示没选中任何项\n         */\n        private int mCheckedIndex = -1;\n\n        public CheckableDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 获取当前选中的菜单项的下标\n         *\n         * @return 负数表示没选中任何项\n         */\n        public int getCheckedIndex() {\n            return mCheckedIndex;\n        }\n\n        /**\n         * 设置选中的菜单项的下班\n         */\n        public CheckableDialogBuilder setCheckedIndex(int checkedIndex) {\n            mCheckedIndex = checkedIndex;\n            return this;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            View result = super.onCreateContent(dialog, parent, context);\n            if (mCheckedIndex > -1 && mCheckedIndex < mMenuItemViews.size()) {\n                mMenuItemViews.get(mCheckedIndex).setChecked(true);\n            }\n            return result;\n        }\n\n        @Override\n        protected void onItemClick(int index) {\n            for (int i = 0; i < mMenuItemViews.size(); i++) {\n                QMUIDialogMenuItemView itemView = mMenuItemViews.get(i);\n                if (i == index) {\n                    itemView.setChecked(true);\n                    mCheckedIndex = index;\n                } else {\n                    itemView.setChecked(false);\n                }\n            }\n        }\n\n        /**\n         * 添加菜单项\n         *\n         * @param items    所有菜单项的文字\n         * @param listener 菜单项的点击事件,可以在点击事件里调用 {@link #setCheckedIndex(int)} 来设置选中某些菜单项\n         */\n        public CheckableDialogBuilder addItems(CharSequence[] items, OnClickListener listener) {\n            for (final CharSequence item : items) {\n                addItem(new ItemViewFactory() {\n                    @Override\n                    public QMUIDialogMenuItemView createItemView(Context context) {\n                        return new QMUIDialogMenuItemView.MarkItemView(context, item);\n                    }\n                }, listener);\n            }\n            return this;\n        }\n    }\n\n    /**\n     * 多选类型的对话框 Builder\n     */\n    public static class MultiCheckableDialogBuilder extends MenuBaseDialogBuilder<MultiCheckableDialogBuilder> {\n\n        /**\n         * 该 int 的每一位标识菜单的每一项是否被选中 (1为选中,0位不选中)\n         */\n        private BitSet mCheckedItems = new BitSet();\n\n        public MultiCheckableDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 设置被选中的菜单项的下标\n         *\n         * @param checkedItems <b>注意: 该 int 参数的每一位标识菜单项的每一项是否被选中</b>\n         *                     <p>如 20 表示选中下标为 1、3 的菜单项, 因为 (2<<1) + (2<<3) = 20</p>\n         */\n        public MultiCheckableDialogBuilder setCheckedItems(BitSet checkedItems) {\n            mCheckedItems.clear();\n            mCheckedItems.or(checkedItems);\n            return this;\n        }\n\n        /**\n         * 设置被选中的菜单项的下标\n         *\n         * @param checkedIndexes 被选中的菜单项的下标组成的数组,如 [1,3] 表示选中下标为 1、3 的菜单项\n         */\n        public MultiCheckableDialogBuilder setCheckedItems(int[] checkedIndexes) {\n            mCheckedItems.clear();\n            if (checkedIndexes != null && checkedIndexes.length > 0) {\n                for (int checkedIndex : checkedIndexes) {\n                    mCheckedItems.set(checkedIndex);\n                }\n            }\n            return this;\n        }\n\n        /**\n         * 添加菜单项\n         *\n         * @param items    所有菜单项的文字\n         * @param listener 菜单项的点击事件,可以在点击事件里调用 {@link #setCheckedItems(int[])}} 来设置选中某些菜单项\n         */\n        public MultiCheckableDialogBuilder addItems(CharSequence[] items, OnClickListener listener) {\n            for (final CharSequence item : items) {\n                addItem(new ItemViewFactory() {\n                    @Override\n                    public QMUIDialogMenuItemView createItemView(Context context) {\n                        return new QMUIDialogMenuItemView.CheckItemView(context, true, item);\n                    }\n                }, listener);\n            }\n            return this;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            View result = super.onCreateContent(dialog, parent, context);\n            for (int i = 0; i < mMenuItemViews.size(); i++) {\n                QMUIDialogMenuItemView itemView = mMenuItemViews.get(i);\n                itemView.setChecked(mCheckedItems.get(i));\n            }\n            return result;\n        }\n\n        @Override\n        protected void onItemClick(int index) {\n            QMUIDialogMenuItemView itemView = mMenuItemViews.get(index);\n            itemView.setChecked(!itemView.isChecked());\n            mCheckedItems.set(index, itemView.isChecked());\n        }\n\n        /**\n         * @return 被选中的菜单项的下标 <b>注意: 如果选中的是1，3项(以0开始)，因为 (2<<1) + (2<<3) = 20</b>\n         */\n        public BitSet getCheckedItemRecord() {\n            return (BitSet) mCheckedItems.clone();\n        }\n\n        /**\n         * @return 被选中的菜单项的下标数组。如果选中的是1，3项(以0开始)，则返回[1,3]\n         */\n        public int[] getCheckedItemIndexes() {\n            ArrayList<Integer> array = new ArrayList<>();\n            int length = mMenuItemViews.size();\n\n            for (int i = 0; i < length; i++) {\n                QMUIDialogMenuItemView itemView = mMenuItemViews.get(i);\n                if (itemView.isChecked()) {\n                    array.add(itemView.getMenuIndex());\n                }\n            }\n            int[] output = new int[array.size()];\n            for (int i = 0; i < array.size(); i++) {\n                output[i] = array.get(i);\n            }\n            return output;\n        }\n\n        protected boolean existCheckedItem() {\n            return !mCheckedItems.isEmpty();\n        }\n    }\n\n    /**\n     * 自定义对话框内容区域的 Builder\n     */\n    public static class CustomDialogBuilder extends QMUIDialogBuilder {\n\n        private int mLayoutId;\n\n        public CustomDialogBuilder(Context context) {\n            super(context);\n        }\n\n        /**\n         * 设置内容区域的 layoutResId\n         */\n        public CustomDialogBuilder setLayout(@LayoutRes int layoutResId) {\n            mLayoutId = layoutResId;\n            return this;\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n            return LayoutInflater.from(context).inflate(mLayoutId, parent, false);\n        }\n    }\n\n    /**\n     * 随键盘升降自动调整 Dialog 高度的 Builder\n     */\n    public static abstract class AutoResizeDialogBuilder extends QMUIDialogBuilder {\n\n        protected ScrollView mScrollView;\n\n        public AutoResizeDialogBuilder(Context context) {\n            super(context);\n            setCheckKeyboardOverlay(true);\n        }\n\n        @Nullable\n        @Override\n        protected View onCreateContent(@NonNull QMUIDialog dialog,@NonNull QMUIDialogView parent, @NonNull Context context) {\n            mScrollView = wrapWithScroll(onBuildContent(dialog, context));\n            return mScrollView;\n        }\n\n        public abstract View onBuildContent(@NonNull QMUIDialog dialog, @NonNull Context context);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogAction.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.util.TypedValue;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIButton;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUISpanHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\n/**\n * @author cginechen\n * @date 2015-10-20\n */\npublic class QMUIDialogAction {\n\n    @IntDef({ACTION_PROP_NEGATIVE, ACTION_PROP_NEUTRAL, ACTION_PROP_POSITIVE})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Prop {\n    }\n\n    //用于标记positive/negative/neutral\n    public static final int ACTION_PROP_POSITIVE = 0;\n    public static final int ACTION_PROP_NEUTRAL = 1;\n    public static final int ACTION_PROP_NEGATIVE = 2;\n\n\n    private CharSequence mStr;\n    private int mIconRes = 0;\n    private int mActionProp = ACTION_PROP_NEUTRAL;\n    private int mSkinTextColorAttr = 0;\n    private int mSkinBackgroundAttr = 0;\n    private int mSkinIconTintColorAttr = 0;\n    private int mSkinSeparatorColorAttr = R.attr.qmui_skin_support_dialog_action_divider_color;\n    private ActionListener mOnClickListener;\n    private QMUIButton mButton;\n    private boolean mIsEnabled = true;\n\n\n    public QMUIDialogAction(Context context, int strRes) {\n        this(context.getResources().getString(strRes));\n    }\n\n    public QMUIDialogAction(CharSequence str) {\n        this(str, null);\n    }\n\n    public QMUIDialogAction(Context context, int strRes, @Nullable ActionListener onClickListener) {\n        this(context.getResources().getString(strRes), onClickListener);\n    }\n\n    public QMUIDialogAction(CharSequence str, @Nullable ActionListener onClickListener) {\n        mStr = str;\n        mOnClickListener = onClickListener;\n    }\n\n    public QMUIDialogAction prop(@Prop int actionProp) {\n        mActionProp = actionProp;\n        return this;\n    }\n\n    public QMUIDialogAction iconRes(@Prop int iconRes) {\n        mIconRes = iconRes;\n        return this;\n    }\n\n    public QMUIDialogAction onClick(ActionListener onClickListener) {\n        mOnClickListener = onClickListener;\n        return this;\n    }\n\n    public QMUIDialogAction skinTextColorAttr(int skinTextColorAttr) {\n        mSkinTextColorAttr = skinTextColorAttr;\n        return this;\n    }\n\n    public QMUIDialogAction skinBackgroundAttr(int skinBackgroundAttr) {\n        mSkinBackgroundAttr = skinBackgroundAttr;\n        return this;\n    }\n\n    public QMUIDialogAction skinIconTintColorAttr(int skinIconTintColorAttr) {\n        mSkinIconTintColorAttr = skinIconTintColorAttr;\n        return this;\n    }\n\n    /**\n     * inner usage\n     * @param skinSeparatorColorAttr\n     * @return\n     */\n    QMUIDialogAction skinSeparatorColorAttr(int skinSeparatorColorAttr){\n        mSkinSeparatorColorAttr = skinSeparatorColorAttr;\n        return this;\n    }\n\n    public QMUIDialogAction setEnabled(boolean enabled) {\n        mIsEnabled = enabled;\n        if (mButton != null) {\n            mButton.setEnabled(enabled);\n        }\n        return this;\n    }\n\n    public QMUIButton buildActionView(final QMUIDialog dialog, final int index) {\n        mButton = generateActionButton(dialog.getContext(), mStr, mIconRes,\n                mSkinBackgroundAttr, mSkinTextColorAttr, mSkinIconTintColorAttr);\n        mButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if (mOnClickListener != null && mButton.isEnabled()) {\n                    mOnClickListener.onClick(dialog, index);\n                }\n            }\n        });\n        return mButton;\n    }\n\n    /**\n     * 生成适用于对话框的按钮\n     */\n    private QMUIButton generateActionButton(Context context, CharSequence text, int iconRes,\n                                            int skinBackgroundAttr, int skinTextColorAttr, int iconTintColor) {\n        QMUIButton button = new QMUIButton(context);\n        button.setBackground(null);\n        button.setMinHeight(0);\n        button.setMinimumHeight(0);\n        button.setChangeAlphaWhenDisable(true);\n        button.setChangeAlphaWhenPress(true);\n        TypedArray a = context.obtainStyledAttributes(\n                null, R.styleable.QMUIDialogActionStyleDef, R.attr.qmui_dialog_action_style, 0);\n        int count = a.getIndexCount();\n        int paddingHor = 0, iconSpace = 0;\n        ColorStateList negativeTextColor = null, positiveTextColor = null;\n        for (int i = 0; i < count; i++) {\n            int attr = a.getIndex(i);\n            if (attr == R.styleable.QMUIDialogActionStyleDef_android_gravity) {\n                button.setGravity(a.getInt(attr, -1));\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_android_textColor) {\n                button.setTextColor(a.getColorStateList(attr));\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_android_textSize) {\n                button.setTextSize(TypedValue.COMPLEX_UNIT_PX, a.getDimensionPixelSize(attr, 0));\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_qmui_dialog_action_button_padding_horizontal) {\n                paddingHor = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_android_background) {\n                button.setBackground(a.getDrawable(attr));\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_android_minWidth) {\n                int miniWidth = a.getDimensionPixelSize(attr, 0);\n                button.setMinWidth(miniWidth);\n                button.setMinimumWidth(miniWidth);\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_qmui_dialog_positive_action_text_color) {\n                positiveTextColor = a.getColorStateList(attr);\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_qmui_dialog_negative_action_text_color) {\n                negativeTextColor = a.getColorStateList(attr);\n            } else if (attr == R.styleable.QMUIDialogActionStyleDef_qmui_dialog_action_icon_space) {\n                iconSpace = a.getDimensionPixelSize(attr, 0);\n            } else if (attr == R.styleable.QMUITextCommonStyleDef_android_textStyle) {\n                int styleIndex = a.getInt(attr, -1);\n                button.setTypeface(null, styleIndex);\n            }\n        }\n\n        a.recycle();\n        button.setPadding(paddingHor, 0, paddingHor, 0);\n        if (iconRes <= 0) {\n            button.setText(text);\n        } else {\n            button.setText(QMUISpanHelper.generateSideIconText(\n                    true, iconSpace, text, ContextCompat.getDrawable(context, iconRes), iconTintColor, button));\n        }\n\n\n        button.setClickable(true);\n        button.setEnabled(mIsEnabled);\n\n        if (mActionProp == ACTION_PROP_NEGATIVE) {\n            button.setTextColor(negativeTextColor);\n            if (skinTextColorAttr == 0) {\n                skinTextColorAttr = R.attr.qmui_skin_support_dialog_negative_action_text_color;\n            }\n        } else if (mActionProp == ACTION_PROP_POSITIVE) {\n            button.setTextColor(positiveTextColor);\n            if (skinTextColorAttr == 0) {\n                skinTextColorAttr = R.attr.qmui_skin_support_dialog_positive_action_text_color;\n            }\n        } else {\n            if (skinTextColorAttr == 0) {\n                skinTextColorAttr = R.attr.qmui_skin_support_dialog_action_text_color;\n            }\n        }\n        QMUISkinValueBuilder skinValueBuilder = QMUISkinValueBuilder.acquire();\n        skinBackgroundAttr = skinBackgroundAttr == 0 ? R.attr.qmui_skin_support_dialog_action_bg : skinBackgroundAttr;\n        skinValueBuilder.background(skinBackgroundAttr);\n        skinValueBuilder.textColor(skinTextColorAttr);\n        if(mSkinSeparatorColorAttr != 0){\n            skinValueBuilder.topSeparator(mSkinSeparatorColorAttr);\n            skinValueBuilder.leftSeparator(mSkinSeparatorColorAttr);\n        }\n        QMUISkinHelper.setSkinValue(button, skinValueBuilder);\n        skinValueBuilder.release();\n        return button;\n    }\n\n    public int getActionProp() {\n        return mActionProp;\n    }\n\n    public interface ActionListener {\n        void onClick(QMUIDialog dialog, int index);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogBlockBuilder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUIWrapContentScrollView;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport androidx.annotation.Nullable;\n\n/**\n * @author cginechen\n * @date 2015-12-12\n */\npublic class QMUIDialogBlockBuilder extends QMUIDialogBuilder<QMUIDialogBlockBuilder> {\n    private CharSequence mContent;\n\n\n    public QMUIDialogBlockBuilder(Context context) {\n        super(context);\n        setActionDivider(1, R.attr.qmui_skin_support_dialog_action_divider_color, 0, 0);\n    }\n\n\n    public QMUIDialogBlockBuilder setContent(CharSequence content) {\n        mContent = content;\n        return this;\n    }\n\n    public QMUIDialogBlockBuilder setContent(int contentRes) {\n        mContent = getBaseContext().getResources().getString(contentRes);\n        return this;\n    }\n\n    @Nullable\n    @Override\n    protected View onCreateTitle(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n        View result = super.onCreateTitle(dialog, parent, context);\n        if(result != null && (mContent == null || mContent.length() == 0)){\n            TypedArray a = context.obtainStyledAttributes(null,\n                    R.styleable.QMUIDialogTitleTvCustomDef, R.attr.qmui_dialog_title_style, 0);\n            int count = a.getIndexCount();\n            for (int i = 0; i < count; i++) {\n                int attr = a.getIndex(i);\n                if (attr == R.styleable.QMUIDialogTitleTvCustomDef_qmui_paddingBottomWhenNotContent) {\n                    result.setPadding(\n                            result.getPaddingLeft(),\n                            result.getPaddingTop(),\n                            result.getPaddingRight(),\n                            a.getDimensionPixelSize(attr, result.getPaddingBottom())\n                    );\n                }\n            }\n            a.recycle();\n        }\n        return result;\n    }\n\n    @Override\n    @Nullable\n    protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {\n        if(mContent != null && mContent.length() > 0){\n            TextView contentTv = new QMUISpanTouchFixTextView(context);\n            QMUIResHelper.assignTextViewWithAttr(contentTv, R.attr.qmui_dialog_message_content_style);\n\n            if (!hasTitle()) {\n                TypedArray a = context.obtainStyledAttributes(null,\n                        R.styleable.QMUIDialogMessageTvCustomDef,\n                        R.attr.qmui_dialog_message_content_style, 0);\n                int count = a.getIndexCount();\n                for (int i = 0; i < count; i++) {\n                    int attr = a.getIndex(i);\n                    if (attr == R.styleable.QMUIDialogMessageTvCustomDef_qmui_paddingTopWhenNotTitle) {\n                        contentTv.setPadding(\n                                contentTv.getPaddingLeft(),\n                                a.getDimensionPixelSize(attr, contentTv.getPaddingTop()),\n                                contentTv.getPaddingRight(),\n                                contentTv.getPaddingBottom()\n                        );\n                    }\n                }\n                a.recycle();\n            }\n            contentTv.setText(mContent);\n            return wrapWithScroll(contentTv);\n        }\n        return null;\n    }\n\n    @Override\n    public QMUIDialog create(int style) {\n        setActionContainerOrientation(VERTICAL);\n        return super.create(style);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogBuilder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.LinearLayout;\nimport android.widget.Space;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIButton;\nimport com.qmuiteam.qmui.layout.QMUILinearLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUIWrapContentScrollView;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StyleRes;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\n/**\n * 创建 {@link QMUIDialog} 的 Builder 基类, 不同的 Builder 子类拥有创建不同类型对话框的能力, 具体见子类。\n * <p>该类产生的 Dialog 分为上中下三个部分:</p>\n * <ul>\n * <li>上部分是 title 区域, 支持显示纯文本标题, 通过 {@link #setTitle(int)} 系列方法设置。\n * 子类也可以通过 override {@link #onCreateTitle(QMUIDialog, QMUIDialogView, Context)} 方法自定义</li>\n * <li>中间部分的内容由各个子类决定, 子类通过 override {@link #onCreateContent(QMUIDialog, QMUIDialogView, Context)} 方法自定义。</li>\n * <li>下部分是操作区域, 支持添加操作按钮, 通过 {@link #addAction(int, int, QMUIDialogAction.ActionListener)} 系列方法添加。\n * 子类也可以通过 override {@link #onCreateOperatorLayout(QMUIDialog, QMUIDialogView, Context)} 方法自定义。\n * 其中操作按钮有内联和块级之分, 也有普通、正向、反向之分, 具体见 {@link QMUIDialogAction}\n * </li>\n * </ul>\n *\n * @author cginechen\n * @date 2015-10-20\n */\npublic abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {\n\n    @IntDef({HORIZONTAL, VERTICAL})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Orientation {\n    }\n\n    public static final int HORIZONTAL = 0;\n    public static final int VERTICAL = 1;\n    /**\n     * A global theme provider, use to distinguish theme from different builder type\n     */\n    private static OnProvideDefaultTheme sOnProvideDefaultTheme = null;\n\n    public static void setOnProvideDefaultTheme(OnProvideDefaultTheme onProvideDefaultTheme) {\n        QMUIDialogBuilder.sOnProvideDefaultTheme = onProvideDefaultTheme;\n    }\n\n    private Context mContext;\n    protected QMUIDialog mDialog;\n    protected String mTitle;\n    private boolean mCancelable = true;\n    private boolean mCanceledOnTouchOutside = true;\n\n    protected QMUIDialogRootLayout mRootView;\n    protected QMUIDialogView mDialogView;\n    protected List<QMUIDialogAction> mActions = new ArrayList<>();\n    private QMUIDialogView.OnDecorationListener mOnDecorationListener;\n\n    @Orientation private int mActionContainerOrientation = HORIZONTAL;\n    private boolean mChangeAlphaForPressOrDisable = true;\n    private int mActionDividerThickness = 0;\n    private int mActionDividerColorAttr = R.attr.qmui_skin_support_dialog_action_divider_color;\n    private int mActionDividerInsetStart = 0;\n    private int mActionDividerInsetEnd = 0;\n    private int mActionDividerColor = 0;\n    private boolean mCheckKeyboardOverlay = false;\n    private QMUISkinManager mSkinManager;\n    private float mMaxPercent = 0.75f;\n\n    public QMUIDialogBuilder(Context context) {\n        this.mContext = context;\n    }\n\n    public Context getBaseContext() {\n        return mContext;\n    }\n\n    /**\n     * 设置对话框顶部的标题文字\n     */\n    @SuppressWarnings(\"unchecked\")\n    public T setTitle(String title) {\n        if (title != null && title.length() > 0) {\n            this.mTitle = title + mContext.getString(R.string.qmui_tool_fixellipsize);\n        }\n        return (T) this;\n    }\n\n    /**\n     * 设置对话框顶部的标题文字\n     */\n    public T setTitle(int resId) {\n        return setTitle(mContext.getResources().getString(resId));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setCancelable(boolean cancelable) {\n        mCancelable = cancelable;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setCanceledOnTouchOutside(boolean canceledOnTouchOutside) {\n        mCanceledOnTouchOutside = canceledOnTouchOutside;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setOnDecorationListener(QMUIDialogView.OnDecorationListener onDecorationListener) {\n        mOnDecorationListener = onDecorationListener;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setActionContainerOrientation(int actionContainerOrientation) {\n        mActionContainerOrientation = actionContainerOrientation;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setChangeAlphaForPressOrDisable(boolean changeAlphaForPressOrDisable) {\n        mChangeAlphaForPressOrDisable = changeAlphaForPressOrDisable;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setActionDivider(int thickness, int colorAttr, int startInset, int endInset) {\n        mActionDividerThickness = thickness;\n        mActionDividerColorAttr = colorAttr;\n        mActionDividerInsetStart = startInset;\n        mActionDividerInsetEnd = endInset;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setActionDividerInsetAndThickness(int thickness, int startInset, int endInset){\n        mActionDividerThickness = thickness;\n        mActionDividerInsetStart = startInset;\n        mActionDividerInsetEnd = endInset;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setActionDividerColorAttr(int colorAttr){\n        mActionDividerColorAttr = colorAttr;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setActionDividerColor(int color){\n        mActionDividerColor = color;\n        mActionDividerColorAttr = 0;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setCheckKeyboardOverlay(boolean checkKeyboardOverlay) {\n        mCheckKeyboardOverlay = checkKeyboardOverlay;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setSkinManager(@Nullable QMUISkinManager skinManager) {\n        mSkinManager = skinManager;\n        return (T) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T setMaxPercent(float maxPercent) {\n        mMaxPercent = maxPercent;\n        return (T) this;\n    }\n\n    //region 添加action\n\n    /**\n     * 添加对话框底部的操作按钮\n     */\n    @SuppressWarnings(\"unchecked\")\n    public T addAction(@Nullable QMUIDialogAction action) {\n        if (action != null) {\n            mActions.add(action);\n        }\n\n        return (T) this;\n    }\n\n    /**\n     * 添加无图标正常类型的操作按钮\n     *\n     * @param strResId 文案\n     * @param listener 点击回调事件\n     */\n    public T addAction(int strResId, QMUIDialogAction.ActionListener listener) {\n        return addAction(0, strResId, listener);\n    }\n\n    /**\n     * 添加无图标正常类型的操作按钮\n     *\n     * @param str      文案\n     * @param listener 点击回调事件\n     */\n    public T addAction(CharSequence str, QMUIDialogAction.ActionListener listener) {\n        return addAction(0, str, QMUIDialogAction.ACTION_PROP_NEUTRAL, listener);\n    }\n\n\n    /**\n     * 添加普通类型的操作按钮\n     *\n     * @param iconResId 图标\n     * @param strResId  文案\n     * @param listener  点击回调事件\n     */\n    public T addAction(int iconResId, int strResId, QMUIDialogAction.ActionListener listener) {\n        return addAction(iconResId, strResId, QMUIDialogAction.ACTION_PROP_NEUTRAL, listener);\n    }\n\n    /**\n     * 添加普通类型的操作按钮\n     *\n     * @param iconResId 图标\n     * @param str       文案\n     * @param listener  点击回调事件\n     */\n    public T addAction(int iconResId, CharSequence str, QMUIDialogAction.ActionListener listener) {\n        return addAction(iconResId, str, QMUIDialogAction.ACTION_PROP_NEUTRAL, listener);\n    }\n\n\n    /**\n     * 添加操作按钮\n     *\n     * @param iconRes  图标\n     * @param strRes   文案\n     * @param prop     属性\n     * @param listener 点击回调事件\n     */\n    public T addAction(int iconRes, int strRes, @QMUIDialogAction.Prop int prop, QMUIDialogAction.ActionListener listener) {\n        return addAction(iconRes, mContext.getResources().getString(strRes), prop, listener);\n    }\n\n    /**\n     * 添加操作按钮\n     *\n     * @param iconRes  图标\n     * @param str      文案\n     * @param prop     属性\n     * @param listener 点击回调事件\n     */\n    @SuppressWarnings(\"unchecked\")\n    public T addAction(int iconRes, CharSequence str, @QMUIDialogAction.Prop int prop, QMUIDialogAction.ActionListener listener) {\n        QMUIDialogAction action = new QMUIDialogAction(str)\n                .iconRes(iconRes)\n                .prop(prop)\n                .onClick(listener);\n        mActions.add(action);\n        return (T) this;\n    }\n\n\n    //endregion\n\n    /**\n     * 判断对话框是否需要显示title\n     *\n     * @return 是否有title\n     */\n    protected boolean hasTitle() {\n        return mTitle != null && mTitle.length() != 0;\n    }\n\n    /**\n     * 产生一个 Dialog 并显示出来\n     */\n    public QMUIDialog show() {\n        final QMUIDialog dialog = create();\n        dialog.show();\n        return dialog;\n    }\n\n    /**\n     * 只产生一个 Dialog, 不显示出来\n     *\n     * @see #create(int)\n     */\n    public QMUIDialog create() {\n        if (sOnProvideDefaultTheme != null) {\n            int theme = sOnProvideDefaultTheme.getThemeForBuilder(this);\n            if (theme > 0) {\n                return create(theme);\n            }\n        }\n        return create(R.style.QMUI_Dialog);\n    }\n\n    /**\n     * 产生一个Dialog，但不显示出来。\n     *\n     * @param style Dialog 的样式\n     * @see #create()\n     */\n    @SuppressLint(\"InflateParams\")\n    public QMUIDialog create(@StyleRes int style) {\n        mDialog = new QMUIDialog(mContext, style);\n        Context dialogContext = mDialog.getContext();\n\n        mDialogView = onCreateDialogView(dialogContext);\n        mRootView = new QMUIDialogRootLayout(dialogContext, mDialogView, onCreateDialogLayoutParams());\n        mRootView.setCheckKeyboardOverlay(mCheckKeyboardOverlay);\n        mRootView.setOverlayOccurInMeasureCallback(new QMUIDialogRootLayout.OverlayOccurInMeasureCallback() {\n            @Override\n            public void call() {\n                onOverlayOccurredInMeasure();\n            }\n        });\n        mRootView.setMaxPercent(mMaxPercent);\n        configRootLayout(mRootView);\n        mDialogView = mRootView.getDialogView();\n        mDialogView.setOnDecorationListener(mOnDecorationListener);\n        // title\n        View titleView = onCreateTitle(mDialog, mDialogView, dialogContext);\n        View operatorLayout = onCreateOperatorLayout(mDialog, mDialogView, dialogContext);\n        View contentLayout = onCreateContent(mDialog, mDialogView, dialogContext);\n        checkAndSetId(titleView, R.id.qmui_dialog_title_id);\n        checkAndSetId(operatorLayout, R.id.qmui_dialog_operator_layout_id);\n        checkAndSetId(contentLayout, R.id.qmui_dialog_content_id);\n\n        // chain\n        if (titleView != null) {\n            ConstraintLayout.LayoutParams lp = onCreateTitleLayoutParams(dialogContext);\n            if (contentLayout != null) {\n                lp.bottomToTop = contentLayout.getId();\n            } else if (operatorLayout != null) {\n                lp.bottomToTop = operatorLayout.getId();\n            } else {\n                lp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n            mDialogView.addView(titleView, lp);\n        }\n\n        if (contentLayout != null) {\n            ConstraintLayout.LayoutParams lp = onCreateContentLayoutParams(dialogContext);\n            if (titleView != null) {\n                lp.topToBottom = titleView.getId();\n            } else {\n                lp.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n\n            if (operatorLayout != null) {\n                lp.bottomToTop = operatorLayout.getId();\n            } else {\n                lp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n            mDialogView.addView(contentLayout, lp);\n        }\n\n        if (operatorLayout != null) {\n            ConstraintLayout.LayoutParams lp = onCreateOperatorLayoutLayoutParams(dialogContext);\n            if (contentLayout != null) {\n                lp.topToBottom = contentLayout.getId();\n            } else if (titleView != null) {\n                lp.topToBottom = titleView.getId();\n            } else {\n                lp.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n            mDialogView.addView(operatorLayout, lp);\n        }\n\n        mDialog.addContentView(mRootView, new ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        mDialog.setCancelable(mCancelable);\n        mDialog.setCanceledOnTouchOutside(mCanceledOnTouchOutside);\n        mDialog.setSkinManager(mSkinManager);\n        onAfterCreate(mDialog, mRootView, dialogContext);\n        return mDialog;\n    }\n\n    protected void onAfterCreate(@NonNull QMUIDialog dialog, @NonNull QMUIDialogRootLayout rootLayout, @NonNull Context context){\n\n    }\n\n    protected void onOverlayOccurredInMeasure(){\n\n    }\n\n    private void checkAndSetId(@Nullable View view, int id) {\n        if (view != null && view.getId() == View.NO_ID) {\n            view.setId(id);\n        }\n    }\n\n    protected void configRootLayout(@NonNull QMUIDialogRootLayout rootLayout){\n\n    }\n\n    protected void skinConfigDialogView(QMUIDialogView dialogView){\n        QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n        valueBuilder.background(R.attr.qmui_skin_support_dialog_bg);\n        QMUISkinHelper.setSkinValue(dialogView, valueBuilder);\n        QMUISkinValueBuilder.release(valueBuilder);\n    }\n    protected void skinConfigTitleView(TextView titleView){\n        QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n        valueBuilder.textColor(R.attr.qmui_skin_support_dialog_title_text_color);\n        QMUISkinHelper.setSkinValue(titleView, valueBuilder);\n        QMUISkinValueBuilder.release(valueBuilder);\n    }\n    protected void skinConfigActionContainer(ViewGroup actionContainer){\n        QMUISkinValueBuilder valueBuilder = QMUISkinValueBuilder.acquire();\n        valueBuilder.topSeparator(R.attr.qmui_skin_support_dialog_action_container_separator_color);\n        QMUISkinHelper.setSkinValue(actionContainer, valueBuilder);\n        QMUISkinValueBuilder.release(valueBuilder);\n    }\n\n    @NonNull\n    protected QMUIDialogView onCreateDialogView(@NonNull Context context){\n        QMUIDialogView dialogView = new QMUIDialogView(context);\n        dialogView.setBackground(QMUIResHelper.getAttrDrawable(context, R.attr.qmui_skin_support_dialog_bg));\n        dialogView.setRadius(QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_radius));\n        skinConfigDialogView(dialogView);\n        return dialogView;\n    }\n\n    @NonNull\n    protected FrameLayout.LayoutParams onCreateDialogLayoutParams() {\n        return new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    @Nullable\n    protected View onCreateTitle(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {\n        if (hasTitle()) {\n            TextView tv = new QMUISpanTouchFixTextView(context);\n            tv.setId(R.id.qmui_dialog_title_id);\n            tv.setText(mTitle);\n            QMUIResHelper.assignTextViewWithAttr(tv, R.attr.qmui_dialog_title_style);\n            skinConfigTitleView(tv);\n            return tv;\n        }\n        return null;\n    }\n\n    @NonNull\n    protected ConstraintLayout.LayoutParams onCreateTitleLayoutParams(@NonNull Context context) {\n        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.verticalChainStyle = ConstraintLayout.LayoutParams.CHAIN_PACKED;\n        return lp;\n    }\n\n\n    @Nullable\n    protected abstract View onCreateContent(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context);\n\n\n    protected QMUIWrapContentScrollView wrapWithScroll(@NonNull View view){\n        QMUIWrapContentScrollView scrollView = new QMUIWrapContentScrollView(view.getContext());\n        scrollView.addView(view);\n        scrollView.setVerticalScrollBarEnabled(false);\n        return scrollView;\n    }\n\n    protected ConstraintLayout.LayoutParams onCreateContentLayoutParams(@NonNull Context context) {\n        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.constrainedHeight = true;\n        return lp;\n    }\n\n\n    @Nullable\n    protected View onCreateOperatorLayout(@NonNull final QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {\n        int size = mActions.size();\n        if (size > 0) {\n            TypedArray a = context.obtainStyledAttributes(null, R.styleable.QMUIDialogActionContainerCustomDef, R.attr.qmui_dialog_action_container_style, 0);\n            int count = a.getIndexCount();\n            int justifyContent = 1, spaceCustomIndex = 0;\n            int actionHeight = -1, actionSpace = 0;\n            for (int i = 0; i < count; i++) {\n                int attr = a.getIndex(i);\n                if (attr == R.styleable.QMUIDialogActionContainerCustomDef_qmui_dialog_action_container_justify_content) {\n                    justifyContent = a.getInteger(attr, justifyContent);\n                } else if (attr == R.styleable.QMUIDialogActionContainerCustomDef_qmui_dialog_action_container_custom_space_index) {\n                    spaceCustomIndex = a.getInteger(attr, 0);\n                } else if (attr == R.styleable.QMUIDialogActionContainerCustomDef_qmui_dialog_action_space) {\n                    actionSpace = a.getDimensionPixelSize(attr, 0);\n                } else if (attr == R.styleable.QMUIDialogActionContainerCustomDef_qmui_dialog_action_height) {\n                    actionHeight = a.getDimensionPixelSize(attr, 0);\n                }\n            }\n            a.recycle();\n            int spaceInsertPos = -1;\n            if (mActionContainerOrientation != VERTICAL) {\n                if (justifyContent == 0) {\n                    spaceInsertPos = size;\n                } else if (justifyContent == 1) {\n                    spaceInsertPos = 0;\n                } else if (justifyContent == 3) {\n                    spaceInsertPos = spaceCustomIndex;\n                }\n            }\n\n            final QMUILinearLayout layout = new QMUILinearLayout(context, null, R.attr.qmui_dialog_action_container_style);\n            layout.setId(R.id.qmui_dialog_operator_layout_id);\n            layout.setOrientation(mActionContainerOrientation == VERTICAL ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);\n            skinConfigActionContainer(layout);\n\n            for (int i = 0; i < size; i++) {\n                if (spaceInsertPos == i) {\n                    layout.addView(createActionContainerSpace(context));\n                }\n                QMUIDialogAction action = mActions.get(i);\n                action.skinSeparatorColorAttr(mActionDividerColorAttr);\n                LinearLayout.LayoutParams actionLp;\n                if (mActionContainerOrientation == VERTICAL) {\n                    actionLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, actionHeight);\n                } else {\n                    actionLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, actionHeight);\n                    if (spaceInsertPos >= 0) {\n                        if (i >= spaceInsertPos) {\n                            actionLp.leftMargin = actionSpace;\n                        } else {\n                            actionLp.rightMargin = actionSpace;\n                        }\n                    }\n                    if (justifyContent == 2) {\n                        actionLp.weight = 1;\n                    }\n                }\n                QMUIButton actionView = action.buildActionView(mDialog, i);\n\n                // add divider\n                if (mActionDividerThickness > 0 && i > 0 && spaceInsertPos != i) {\n                    int color = mActionDividerColorAttr == 0 ? mActionDividerColor :\n                            QMUISkinHelper.getSkinColor(actionView, mActionDividerColorAttr);\n                    if (mActionContainerOrientation == VERTICAL) {\n                        actionView.onlyShowTopDivider(mActionDividerInsetStart,\n                                mActionDividerInsetEnd, mActionDividerThickness, color);\n                    } else {\n                        actionView.onlyShowLeftDivider(mActionDividerInsetStart,\n                                mActionDividerInsetEnd, mActionDividerThickness, color);\n                    }\n                }\n\n                actionView.setChangeAlphaWhenDisable(mChangeAlphaForPressOrDisable);\n                actionView.setChangeAlphaWhenPress(mChangeAlphaForPressOrDisable);\n                layout.addView(actionView, actionLp);\n            }\n\n            if (spaceInsertPos == size) {\n                layout.addView(createActionContainerSpace(context));\n            }\n\n            if (mActionContainerOrientation == HORIZONTAL) {\n                layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {\n                    @Override\n                    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {\n                        int width = right - left;\n                        int childCount = layout.getChildCount();\n                        if (childCount > 0) {\n                            View lastChild = layout.getChildAt(childCount - 1);\n                            // 如果ActionButton的宽度过宽，则减小padding\n                            if (lastChild.getRight() > width) {\n                                int childPaddingHor = Math.max(0, lastChild.getPaddingLeft() - QMUIDisplayHelper.dp2px(mContext, 3));\n                                for (int i = 0; i < childCount; i++) {\n                                    layout.getChildAt(i).setPadding(childPaddingHor, 0, childPaddingHor, 0);\n                                }\n                            }\n                        }\n\n                    }\n                });\n            }\n            return layout;\n        }\n        return null;\n    }\n\n    @NonNull\n    protected ConstraintLayout.LayoutParams onCreateOperatorLayoutLayoutParams(@NonNull Context context) {\n        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.verticalChainStyle = ConstraintLayout.LayoutParams.CHAIN_PACKED;\n        return lp;\n    }\n\n    private View createActionContainerSpace(Context context) {\n        Space space = new Space(context);\n        LinearLayout.LayoutParams spaceLp = new LinearLayout.LayoutParams(0, 0);\n        spaceLp.weight = 1;\n        space.setLayoutParams(spaceLp);\n        return space;\n    }\n\n\n    public List<QMUIDialogAction> getPositiveAction() {\n        List<QMUIDialogAction> output = new ArrayList<>();\n        for (QMUIDialogAction action : mActions) {\n            if (action.getActionProp() == QMUIDialogAction.ACTION_PROP_POSITIVE) {\n                output.add(action);\n            }\n        }\n        return output;\n    }\n\n    public interface OnProvideDefaultTheme {\n        int getThemeForBuilder(QMUIDialogBuilder builder);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogMenuItemView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.text.TextUtils;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport androidx.appcompat.widget.AppCompatImageView;\n\n\n/**\n * 菜单类型的对话框的item\n *\n * @author chantchen\n * @date 2016-1-20\n */\n\npublic class QMUIDialogMenuItemView extends QMUIConstraintLayout {\n    private int index = -1;\n    private MenuItemViewListener mListener;\n    private boolean mIsChecked = false;\n\n    public QMUIDialogMenuItemView(Context context) {\n        super(context, null, R.attr.qmui_dialog_menu_item_style);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.background(R.attr.qmui_skin_support_s_dialog_menu_item_bg);\n        QMUISkinHelper.setSkinValue(this, builder);\n        QMUISkinValueBuilder.release(builder);\n    }\n\n    @SuppressLint(\"CustomViewStyleable\")\n    public static TextView createItemTextView(Context context) {\n        TextView tv = new QMUISpanTouchFixTextView(context);\n        TypedArray a = context.obtainStyledAttributes(null, R.styleable.QMUIDialogMenuTextStyleDef, R.attr.qmui_dialog_menu_item_style, 0);\n        int count = a.getIndexCount();\n        for (int i = 0; i < count; i++) {\n            int attr = a.getIndex(i);\n            if (attr == R.styleable.QMUIDialogMenuTextStyleDef_android_gravity) {\n                tv.setGravity(a.getInt(attr, -1));\n            } else if (attr == R.styleable.QMUIDialogMenuTextStyleDef_android_textColor) {\n                tv.setTextColor(a.getColorStateList(attr));\n            } else if (attr == R.styleable.QMUIDialogMenuTextStyleDef_android_textSize) {\n                tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, a.getDimensionPixelSize(attr, 0));\n            }\n        }\n        a.recycle();\n        tv.setId(View.generateViewId());\n        tv.setSingleLine(true);\n        tv.setEllipsize(TextUtils.TruncateAt.MIDDLE);\n        tv.setDuplicateParentStateEnabled(false);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.textColor(R.attr.qmui_skin_support_dialog_menu_item_text_color);\n        QMUISkinHelper.setSkinValue(tv, builder);\n        QMUISkinValueBuilder.release(builder);\n        return tv;\n    }\n\n    public int getMenuIndex() {\n        return this.index;\n    }\n\n    public void setMenuIndex(int index) {\n        this.index = index;\n    }\n\n    protected void notifyCheckChange(boolean isChecked) {\n\n    }\n\n    public boolean isChecked() {\n        return mIsChecked;\n    }\n\n    public void setChecked(boolean checked) {\n        mIsChecked = checked;\n        notifyCheckChange(mIsChecked);\n    }\n\n    public void setListener(MenuItemViewListener listener) {\n        if (!isClickable()) {\n            setClickable(true);\n        }\n        mListener = listener;\n    }\n\n    @Override\n    public boolean performClick() {\n        if (mListener != null) {\n            mListener.onClick(index);\n        }\n        return super.performClick();\n    }\n\n    public interface MenuItemViewListener {\n        void onClick(int index);\n    }\n\n    public static class TextItemView extends QMUIDialogMenuItemView {\n        protected TextView mTextView;\n\n        public TextItemView(Context context) {\n            super(context);\n            init();\n        }\n\n        public TextItemView(Context context, CharSequence text) {\n            super(context);\n            init();\n            setText(text);\n        }\n\n        private void init() {\n            mTextView = createItemTextView(getContext());\n            LayoutParams lp = new LayoutParams(0, 0);\n            lp.leftToLeft = LayoutParams.PARENT_ID;\n            lp.rightToRight = LayoutParams.PARENT_ID;\n            lp.bottomToBottom = LayoutParams.PARENT_ID;\n            lp.topToTop = LayoutParams.PARENT_ID;\n            addView(mTextView, lp);\n        }\n\n        public void setText(CharSequence text) {\n            mTextView.setText(text);\n        }\n\n        @Deprecated\n        public void setTextColor(int color) {\n            mTextView.setTextColor(color);\n        }\n\n        public void setTextColorAttr(int colorAttr) {\n            int color = QMUISkinHelper.getSkinColor(this, colorAttr);\n            mTextView.setTextColor(color);\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            builder.textColor(colorAttr);\n            QMUISkinHelper.setSkinValue(mTextView, builder);\n            QMUISkinValueBuilder.release(builder);\n        }\n    }\n\n    public static class MarkItemView extends QMUIDialogMenuItemView {\n        private Context mContext;\n        private TextView mTextView;\n        private AppCompatImageView mCheckedView;\n\n        @SuppressLint(\"CustomViewStyleable\")\n        public MarkItemView(Context context) {\n            super(context);\n            mContext = context;\n            mCheckedView = new AppCompatImageView(mContext);\n            mCheckedView.setId(View.generateViewId());\n\n            TypedArray a = context.obtainStyledAttributes(null, R.styleable.QMUIDialogMenuMarkDef,\n                    R.attr.qmui_dialog_menu_item_style, 0);\n            int markMarginHor = 0;\n            int count = a.getIndexCount();\n            for (int i = 0; i < count; i++) {\n                int attr = a.getIndex(i);\n                if (attr == R.styleable.QMUIDialogMenuMarkDef_qmui_dialog_menu_item_check_mark_margin_hor) {\n                    markMarginHor = a.getDimensionPixelSize(attr, 0);\n                } else if (attr == R.styleable.QMUIDialogMenuMarkDef_qmui_dialog_menu_item_mark_drawable) {\n                    mCheckedView.setImageDrawable(QMUIResHelper.getAttrDrawable(context, a, attr));\n                }\n            }\n            a.recycle();\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            builder.src(R.attr.qmui_skin_support_dialog_mark_drawable);\n            QMUISkinHelper.setSkinValue(mCheckedView, builder);\n            QMUISkinValueBuilder.release(builder);\n\n            LayoutParams checkLp = new LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            checkLp.rightToRight = LayoutParams.PARENT_ID;\n            checkLp.topToTop = LayoutParams.PARENT_ID;\n            checkLp.bottomToBottom = LayoutParams.PARENT_ID;\n            addView(mCheckedView, checkLp);\n\n\n            mTextView = createItemTextView(mContext);\n            LayoutParams tvLp = new LayoutParams(0, 0);\n            tvLp.leftToLeft = LayoutParams.PARENT_ID;\n            tvLp.topToTop = LayoutParams.PARENT_ID;\n            tvLp.bottomToBottom = LayoutParams.PARENT_ID;\n            tvLp.rightToLeft = mCheckedView.getId();\n            tvLp.rightMargin = markMarginHor;\n            addView(mTextView, tvLp);\n            mCheckedView.setVisibility(INVISIBLE);\n        }\n\n        public MarkItemView(Context context, CharSequence text) {\n            this(context);\n            setText(text);\n        }\n\n        public void setText(CharSequence text) {\n            mTextView.setText(text);\n        }\n\n        @Override\n        protected void notifyCheckChange(boolean isChecked) {\n            mCheckedView.setVisibility(isChecked ? VISIBLE : INVISIBLE);\n        }\n    }\n\n    @SuppressLint({\"ViewConstructor\", \"CustomViewStyleable\"})\n    public static class CheckItemView extends QMUIDialogMenuItemView {\n        private Context mContext;\n        private TextView mTextView;\n        private AppCompatImageView mCheckedView;\n\n        public CheckItemView(Context context, boolean right) {\n            super(context);\n            mContext = context;\n            mCheckedView = new AppCompatImageView(mContext);\n            mCheckedView.setId(View.generateViewId());\n\n            TypedArray a = context.obtainStyledAttributes(null, R.styleable.QMUIDialogMenuCheckDef,\n                    R.attr.qmui_dialog_menu_item_style, 0);\n            int markMarginHor = 0;\n            int count = a.getIndexCount();\n            for (int i = 0; i < count; i++) {\n                int attr = a.getIndex(i);\n                if (attr == R.styleable.QMUIDialogMenuCheckDef_qmui_dialog_menu_item_check_mark_margin_hor) {\n                    markMarginHor = a.getDimensionPixelSize(attr, 0);\n                } else if (attr == R.styleable.QMUIDialogMenuCheckDef_qmui_dialog_menu_item_check_drawable) {\n                    mCheckedView.setImageDrawable(QMUIResHelper.getAttrDrawable(context, a, attr));\n                }\n            }\n            a.recycle();\n\n            LayoutParams checkLp = new LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            checkLp.topToTop = LayoutParams.PARENT_ID;\n            checkLp.bottomToBottom = LayoutParams.PARENT_ID;\n            if(right){\n                checkLp.rightToRight = LayoutParams.PARENT_ID;\n            }else{\n                checkLp.leftToLeft = LayoutParams.PARENT_ID;\n            }\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            builder.src(R.attr.qmui_skin_support_s_dialog_check_drawable);\n            QMUISkinHelper.setSkinValue(mCheckedView, builder);\n            QMUISkinValueBuilder.release(builder);\n            addView(mCheckedView, checkLp);\n\n            mTextView = createItemTextView(mContext);\n            LayoutParams tvLp = new LayoutParams(0, 0);\n            if(right){\n                tvLp.leftToLeft = LayoutParams.PARENT_ID;\n                tvLp.rightToLeft = mCheckedView.getId();\n                tvLp.rightMargin = markMarginHor;\n            }else{\n                tvLp.rightToRight = LayoutParams.PARENT_ID;\n                tvLp.leftToRight = mCheckedView.getId();\n                tvLp.leftMargin = markMarginHor;\n            }\n\n            tvLp.topToTop = LayoutParams.PARENT_ID;\n            tvLp.bottomToBottom = LayoutParams.PARENT_ID;\n            addView(mTextView, tvLp);\n        }\n\n        public CheckItemView(Context context, boolean right, CharSequence text) {\n            this(context, right);\n            setText(text);\n        }\n\n        public void setText(CharSequence text) {\n            mTextView.setText(text);\n        }\n\n        public CharSequence getText() {\n            return mTextView.getText();\n        }\n\n        @Override\n        protected void notifyCheckChange(boolean isChecked) {\n            QMUIViewHelper.safeSetImageViewSelected(mCheckedView, isChecked);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogRootLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.view.MotionEvent;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowHelper;\n\npublic class QMUIDialogRootLayout extends ViewGroup {\n\n    private QMUIDialogView mDialogView;\n    private FrameLayout.LayoutParams mDialogViewLp;\n    private int mMinWidth;\n    private int mMaxWidth;\n    private int mInsetHor;\n    private int mInsetVer;\n    private boolean mCheckKeyboardOverlay = false;\n    private float mMaxPercent = 0.75f;\n    private boolean isOverlayOccurEventNotified = false;\n    private OverlayOccurInMeasureCallback mOverlayOccurInMeasureCallback;\n    private int mLastContentInsetTop = 0;\n\n\n    public QMUIDialogRootLayout(@NonNull Context context, @NonNull QMUIDialogView dialogView,\n                                @Nullable FrameLayout.LayoutParams dialogViewLp) {\n        super(context);\n        mDialogView = dialogView;\n        if (dialogViewLp == null) {\n            dialogViewLp = new FrameLayout.LayoutParams(\n                    LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        }\n        mDialogViewLp = dialogViewLp;\n        addView(mDialogView, dialogViewLp);\n        mMinWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_min_width);\n        mMaxWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_max_width);\n        mInsetHor = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_inset_hor);\n        mInsetVer = QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_inset_ver);\n        setId(R.id.qmui_dialog_root_layout);\n    }\n\n    public void setMinWidth(int minWidth) {\n        mMinWidth = minWidth;\n    }\n\n    public void setMaxWidth(int maxWidth) {\n        mMaxWidth = maxWidth;\n    }\n\n    public void setInsetHor(int insetHor) {\n        mInsetHor = insetHor;\n    }\n\n    public void setInsetVer(int insetVer) {\n        mInsetVer = insetVer;\n    }\n\n    public void setOverlayOccurInMeasureCallback(OverlayOccurInMeasureCallback overlayOccurInMeasureCallback) {\n        mOverlayOccurInMeasureCallback = overlayOccurInMeasureCallback;\n    }\n\n    public void setCheckKeyboardOverlay(boolean checkKeyboardOverlay) {\n        mCheckKeyboardOverlay = checkKeyboardOverlay;\n    }\n\n    public void setMaxPercent(float maxPercent) {\n        mMaxPercent = maxPercent;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int keyboardOverlayHeight = 0;\n        int contentInsetVer = 0;\n        if (mCheckKeyboardOverlay) {\n            Rect visibleInsetRect = QMUIWindowHelper.unSafeGetWindowVisibleInsets(this);\n            Rect contentInsetRect = QMUIWindowHelper.unSafeGetContentInsets(this);\n            if (visibleInsetRect != null) {\n                keyboardOverlayHeight = visibleInsetRect.bottom;\n            }\n            if (contentInsetRect != null) {\n                mLastContentInsetTop = contentInsetRect.top;\n                contentInsetVer = contentInsetRect.top + contentInsetRect.bottom;\n            }\n        }\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\n        int childWidthMeasureSpec, childHeightMeasureSpec;\n        if (mDialogViewLp.width > 0) {\n            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mDialogViewLp.width, MeasureSpec.EXACTLY);\n        } else {\n            int childMaxWidth = Math.min(mMaxWidth, widthSize - 2 * mInsetHor);\n            if (childMaxWidth <= mMinWidth) {\n                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMinWidth, MeasureSpec.EXACTLY);\n            } else if (mDialogViewLp.width == LayoutParams.MATCH_PARENT) {\n                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childMaxWidth, MeasureSpec.EXACTLY);\n            } else {\n                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childMaxWidth, MeasureSpec.AT_MOST);\n            }\n        }\n\n        if (mDialogViewLp.height > 0) {\n            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mDialogViewLp.height, MeasureSpec.EXACTLY);\n        } else {\n            int childMaxHeight;\n            if (keyboardOverlayHeight > 0) {\n                if (getRootView() != null && getRootView().getHeight() > 0) {\n                    // the overlay occurred with this height, we can't change it.\n                    heightSize = getRootView().getHeight();\n                    if (!isOverlayOccurEventNotified) {\n                        isOverlayOccurEventNotified = true;\n                        if (mOverlayOccurInMeasureCallback != null) {\n                            mOverlayOccurInMeasureCallback.call();\n                        }\n                    }\n                }\n                childMaxHeight = Math.max(heightSize - 2 * mInsetVer - keyboardOverlayHeight - contentInsetVer, 0);\n            } else {\n                // use maxPercent to keep dialog from being too high and calculated based on\n                // screen height because height size while change to actual height when multi onMeasure.\n                isOverlayOccurEventNotified = false;\n                childMaxHeight = Math.min(heightSize - 2 * mInsetVer - contentInsetVer,\n                        (int) (QMUIDisplayHelper.getScreenHeight(getContext()) * mMaxPercent - 2 * mInsetVer));\n            }\n            if (mDialogViewLp.height == LayoutParams.MATCH_PARENT) {\n                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childMaxHeight, MeasureSpec.EXACTLY);\n            } else {\n                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childMaxHeight, MeasureSpec.AT_MOST);\n            }\n        }\n        mDialogView.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n        if (mDialogView.getMeasuredWidth() < mMinWidth) {\n            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMinWidth, MeasureSpec.EXACTLY);\n            mDialogView.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n        }\n        // InsetVer works when keyboard overlay occurs\n        setMeasuredDimension(mDialogView.getMeasuredWidth(),\n                mDialogView.getMeasuredHeight() + 2 * mInsetVer + keyboardOverlayHeight + contentInsetVer);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        int w = r - l;\n        int childLeft = (w - mDialogView.getMeasuredWidth()) / 2;\n        mDialogView.layout(childLeft, mInsetVer,\n                childLeft + mDialogView.getMeasuredWidth(),\n                mInsetVer + mDialogView.getMeasuredHeight());\n    }\n\n    public QMUIDialogView getDialogView() {\n        return mDialogView;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        // I think this is a android system bug:\n        // When show keyboard In fullscreen and the content overlaps with keyboard,\n        // then the mAttachInfo.mContentInset.top equals notch's height\n        // but the event's y and draw position has different behavior if notch exist.\n        if (mLastContentInsetTop > 0) {\n            ev.offsetLocation(0, -mLastContentInsetTop);\n        }\n        return super.dispatchTouchEvent(ev);\n    }\n\n    interface OverlayOccurInMeasureCallback {\n        void call();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUIDialogView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\n\n/**\n * Created by cgspine on 2018/2/28.\n */\n\npublic class QMUIDialogView extends QMUIConstraintLayout {\n\n\n    private OnDecorationListener mOnDecorationListener;\n\n    public QMUIDialogView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIDialogView(Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIDialogView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setId(R.id.qmui_dialog_layout);\n    }\n\n    public void setOnDecorationListener(OnDecorationListener onDecorationListener) {\n        mOnDecorationListener = onDecorationListener;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        if (mOnDecorationListener != null) {\n            mOnDecorationListener.onDraw(canvas, this);\n        }\n    }\n\n    @Override\n    public void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        if (mOnDecorationListener != null) {\n            mOnDecorationListener.onDrawOver(canvas, this);\n        }\n    }\n\n    public interface OnDecorationListener {\n        void onDraw(Canvas canvas, QMUIDialogView view);\n\n        void onDrawOver(Canvas canvas, QMUIDialogView view);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUITipDialog.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUILoadingView;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * 提供一个浮层展示在屏幕中间, 一般使用 {@link QMUITipDialog.Builder} 或 {@link QMUITipDialog.CustomBuilder} 生成。\n * <ul>\n * <li>{@link QMUITipDialog.Builder} 提供了一个图标和一行文字的样式, 其中图标有几种类型可选, 见 {@link QMUITipDialog.Builder.IconType}</li>\n * <li>{@link QMUITipDialog.CustomBuilder} 支持传入自定义的 layoutResId, 达到自定义 TipDialog 的效果。</li>\n * </ul>\n *\n * @author cginechen\n * @date 2016-10-14\n */\n\npublic class QMUITipDialog extends QMUIBaseDialog {\n\n\n    public QMUITipDialog(Context context) {\n        this(context, R.style.QMUI_TipDialog);\n    }\n\n    public QMUITipDialog(Context context, int themeResId) {\n        super(context, themeResId);\n        setCanceledOnTouchOutside(false);\n    }\n\n    /**\n     * 生成默认的 {@link QMUITipDialog}\n     * <p>\n     * 提供了一个图标和一行文字的样式, 其中图标有几种类型可选。见 {@link IconType}\n     * </p>\n     *\n     * @see CustomBuilder\n     */\n    public static class Builder {\n        /**\n         * 不显示任何icon\n         */\n        public static final int ICON_TYPE_NOTHING = 0;\n        /**\n         * 显示 Loading 图标\n         */\n        public static final int ICON_TYPE_LOADING = 1;\n        /**\n         * 显示成功图标\n         */\n        public static final int ICON_TYPE_SUCCESS = 2;\n        /**\n         * 显示失败图标\n         */\n        public static final int ICON_TYPE_FAIL = 3;\n        /**\n         * 显示信息图标\n         */\n        public static final int ICON_TYPE_INFO = 4;\n\n        @IntDef({ICON_TYPE_NOTHING, ICON_TYPE_LOADING, ICON_TYPE_SUCCESS, ICON_TYPE_FAIL, ICON_TYPE_INFO})\n        @Retention(RetentionPolicy.SOURCE)\n        public @interface IconType {\n        }\n\n        private @IconType int mCurrentIconType = ICON_TYPE_NOTHING;\n\n        private Context mContext;\n\n        private CharSequence mTipWord;\n\n        private QMUISkinManager mSkinManager;\n\n        public Builder(Context context) {\n            mContext = context;\n        }\n\n        /**\n         * 设置 icon 显示的内容\n         *\n         * @see IconType\n         */\n        public Builder setIconType(@IconType int iconType) {\n            mCurrentIconType = iconType;\n            return this;\n        }\n\n        /**\n         * 设置显示的文案\n         */\n        public Builder setTipWord(CharSequence tipWord) {\n            mTipWord = tipWord;\n            return this;\n        }\n\n        public Builder setSkinManager(@Nullable QMUISkinManager skinManager) {\n            mSkinManager = skinManager;\n            return this;\n        }\n\n        public QMUITipDialog create() {\n            return create(true);\n        }\n\n        public QMUITipDialog create(boolean cancelable) {\n            return create(cancelable, R.style.QMUI_TipDialog);\n        }\n\n        /**\n         * 创建 Dialog, 但没有弹出来, 如果要弹出来, 请调用返回值的 {@link Dialog#show()} 方法\n         *\n         * @param cancelable 按系统返回键是否可以取消\n         * @return 创建的 Dialog\n         */\n        public QMUITipDialog create(boolean cancelable, int style) {\n            QMUITipDialog dialog = new QMUITipDialog(mContext, style);\n            dialog.setCancelable(cancelable);\n            dialog.setSkinManager(mSkinManager);\n            Context dialogContext = dialog.getContext();\n            QMUITipDialogView dialogView = new QMUITipDialogView(dialogContext);\n\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            if (mCurrentIconType == ICON_TYPE_LOADING) {\n                QMUILoadingView loadingView = new QMUILoadingView(dialogContext);\n                loadingView.setColor(QMUIResHelper.getAttrColor(\n                        dialogContext, R.attr.qmui_skin_support_tip_dialog_loading_color));\n\n                loadingView.setSize(QMUIResHelper.getAttrDimen(\n                        dialogContext, R.attr.qmui_tip_dialog_loading_size));\n                builder.tintColor(R.attr.qmui_skin_support_tip_dialog_loading_color);\n                QMUISkinHelper.setSkinValue(loadingView, builder);\n                dialogView.addView(loadingView, onCreateIconOrLoadingLayoutParams(dialogContext));\n\n            } else if (mCurrentIconType == ICON_TYPE_SUCCESS ||\n                    mCurrentIconType == ICON_TYPE_FAIL ||\n                    mCurrentIconType == ICON_TYPE_INFO) {\n                ImageView imageView = new AppCompatImageView(dialogContext);\n\n                builder.clear();\n                Drawable drawable;\n                if (mCurrentIconType == ICON_TYPE_SUCCESS) {\n                    drawable = QMUIResHelper.getAttrDrawable(\n                            dialogContext, R.attr.qmui_skin_support_tip_dialog_icon_success_src);\n                    builder.src( R.attr.qmui_skin_support_tip_dialog_icon_success_src);\n                } else if (mCurrentIconType == ICON_TYPE_FAIL) {\n                    drawable = QMUIResHelper.getAttrDrawable(\n                            dialogContext, R.attr.qmui_skin_support_tip_dialog_icon_error_src);\n                    builder.src(R.attr.qmui_skin_support_tip_dialog_icon_error_src);\n                } else {\n                    drawable = QMUIResHelper.getAttrDrawable(\n                            dialogContext, R.attr.qmui_skin_support_tip_dialog_icon_info_src);\n                    builder.src(R.attr.qmui_skin_support_tip_dialog_icon_info_src);\n                }\n                imageView.setImageDrawable(drawable);\n                QMUISkinHelper.setSkinValue(imageView, builder);\n                dialogView.addView(imageView, onCreateIconOrLoadingLayoutParams(dialogContext));\n\n            }\n\n            if (mTipWord != null && mTipWord.length() > 0) {\n                TextView tipView = new QMUISpanTouchFixTextView(dialogContext);\n                tipView.setEllipsize(TextUtils.TruncateAt.END);\n                tipView.setId(R.id.qmui_tip_content_id);\n                tipView.setGravity(Gravity.CENTER);\n                tipView.setTextSize(TypedValue.COMPLEX_UNIT_PX,\n                        QMUIResHelper.getAttrDimen(dialogContext, R.attr.qmui_tip_dialog_text_size));\n                tipView.setTextColor(QMUIResHelper.getAttrColor(\n                        dialogContext, R.attr.qmui_skin_support_tip_dialog_text_color));\n                tipView.setText(mTipWord);\n\n                builder.clear();\n                builder.textColor(R.attr.qmui_skin_support_tip_dialog_text_color);\n                QMUISkinHelper.setSkinValue(tipView, builder);\n                dialogView.addView(tipView, onCreateTextLayoutParams(dialogContext, mCurrentIconType));\n            }\n            builder.release();\n            dialog.setContentView(dialogView);\n            return dialog;\n        }\n\n        protected LinearLayout.LayoutParams onCreateIconOrLoadingLayoutParams(Context context) {\n            return new LinearLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        }\n\n\n\n        protected LinearLayout.LayoutParams onCreateTextLayoutParams(Context context, @IconType int iconType) {\n            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            if (iconType != ICON_TYPE_NOTHING) {\n                lp.topMargin = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_text_margin_top);\n            }\n            return lp;\n        }\n\n    }\n\n    /**\n     * CustomBuilder with xml layout\n     */\n    public static class CustomBuilder {\n        private Context mContext;\n        private int mContentLayoutId;\n        private QMUISkinManager mSkinManager;\n\n        public CustomBuilder(Context context) {\n            mContext = context;\n        }\n\n        public CustomBuilder setSkinManager(@Nullable QMUISkinManager skinManager) {\n            mSkinManager = skinManager;\n            return this;\n        }\n\n        public CustomBuilder setContent(@LayoutRes int layoutId) {\n            mContentLayoutId = layoutId;\n            return this;\n        }\n\n        public QMUITipDialog create() {\n            QMUITipDialog dialog = new QMUITipDialog(mContext);\n            dialog.setSkinManager(mSkinManager);\n            Context dialogContext = dialog.getContext();\n            QMUITipDialogView tipDialogView = new QMUITipDialogView(dialogContext);\n            LayoutInflater.from(dialogContext).inflate(mContentLayoutId, tipDialogView, true);\n            dialog.setContentView(tipDialogView);\n            return dialog;\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/dialog/QMUITipDialogView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.dialog;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.view.Gravity;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUILinearLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\npublic class QMUITipDialogView extends QMUILinearLayout {\n\n    private final int mMaxWidth;\n    private final int mMiniWidth;\n    private final int mMiniHeight;\n\n    public QMUITipDialogView(Context context) {\n        super(context);\n        setOrientation(VERTICAL);\n        setGravity(Gravity.CENTER_HORIZONTAL);\n        int radius = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_radius);\n        Drawable background = QMUIResHelper.getAttrDrawable(context, R.attr.qmui_skin_support_tip_dialog_bg);\n        int paddingHor = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_padding_horizontal);\n        int paddingVer = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_padding_vertical);\n        setBackground(background);\n        setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n        setRadius(radius);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.background(R.attr.qmui_skin_support_tip_dialog_bg);\n        QMUISkinHelper.setSkinValue(this, builder);\n        builder.release();\n        mMaxWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_max_width);\n        mMiniWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_min_width);\n        mMiniHeight = QMUIResHelper.getAttrDimen(context, R.attr.qmui_tip_dialog_min_height);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        if(widthSize > mMaxWidth){\n            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, widthMode);\n        }\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        boolean needRemeasure = false;\n        if(getMeasuredWidth() < mMiniWidth){\n            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMiniWidth, MeasureSpec.EXACTLY);\n            needRemeasure = true;\n        }\n\n        if(getMeasuredHeight() < mMiniHeight){\n            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMiniHeight, MeasureSpec.EXACTLY);\n            needRemeasure = true;\n        }\n\n        if(needRemeasure){\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/grouplist/QMUICommonListItemView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.grouplist;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.CheckBox;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport androidx.annotation.IntDef;\nimport androidx.appcompat.widget.AppCompatCheckBox;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * 作为通用列表 {@link QMUIGroupListView} 里的 item 使用，也可以单独使用。\n * 支持以下样式:\n * <ul>\n * <li>通过 {@link #setText(CharSequence)} 设置一行文字</li>\n * <li>通过 {@link #setDetailText(CharSequence)} 设置一行说明文字, 并通过 {@link #setOrientation(int)} 设置说明文字的位置,\n * 也可以在 xml 中使用 {@link R.styleable#QMUICommonListItemView_qmui_orientation} 设置。</li>\n * <li>通过 {@link #setAccessoryType(int)} 设置右侧 View 的类型, 可选的类型见 {@link QMUICommonListItemAccessoryType},\n * 也可以在 xml 中使用 {@link R.styleable#QMUICommonListItemView_qmui_accessory_type} 设置。</li>\n * </ul>\n *\n * @author chantchen\n * @date 2015-1-8\n */\npublic class QMUICommonListItemView extends QMUIConstraintLayout {\n\n    /**\n     * 右侧不显示任何东西\n     */\n    public final static int ACCESSORY_TYPE_NONE = 0;\n    /**\n     * 右侧显示一个箭头\n     */\n    public final static int ACCESSORY_TYPE_CHEVRON = 1;\n    /**\n     * 右侧显示一个开关\n     */\n    public final static int ACCESSORY_TYPE_SWITCH = 2;\n    /**\n     * 自定义右侧显示的 View\n     */\n    public final static int ACCESSORY_TYPE_CUSTOM = 3;\n\n    private final static int TIP_SHOW_NOTHING = 0;\n    private final static int TIP_SHOW_RED_POINT = 1;\n    private final static int TIP_SHOW_NEW = 2;\n\n    /**\n     * detailText 在 title 文字的下方\n     */\n    public final static int VERTICAL = 0;\n    /**\n     * detailText 在 item 的右方\n     */\n    public final static int HORIZONTAL = 1;\n\n    /**\n     * TIP 在左边\n     */\n    public final static int TIP_POSITION_LEFT = 0;\n    /**\n     * TIP 在右边\n     */\n    public final static int TIP_POSITION_RIGHT = 1;\n\n    @IntDef({ACCESSORY_TYPE_NONE, ACCESSORY_TYPE_CHEVRON, ACCESSORY_TYPE_SWITCH, ACCESSORY_TYPE_CUSTOM})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface QMUICommonListItemAccessoryType {\n    }\n\n    @IntDef({VERTICAL, HORIZONTAL})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface QMUICommonListItemOrientation {\n    }\n\n    @IntDef({TIP_POSITION_LEFT, TIP_POSITION_RIGHT})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface QMUICommonListItemTipPosition {\n    }\n\n    /**\n     * Item 右侧的 View 的类型\n     */\n    @QMUICommonListItemAccessoryType\n    private int mAccessoryType;\n\n    /**\n     * 控制 detailText 是在 title 文字的下方还是 item 的右方\n     */\n    private int mOrientation = HORIZONTAL;\n\n    /**\n     * 控制红点的位置\n     */\n    @QMUICommonListItemTipPosition\n    private int mTipPosition = TIP_POSITION_LEFT;\n\n\n    protected ImageView mImageView;\n    private ViewGroup mAccessoryView;\n    protected TextView mTextView;\n    protected TextView mDetailTextView;\n    protected CheckBox mSwitch;\n    private ImageView mRedDot;\n    private ImageView mNewTipView;\n    private boolean mDisableSwitchSelf = false;\n\n    private int mTipShown = TIP_SHOW_NOTHING;\n\n    public QMUICommonListItemView(Context context) {\n        this(context, null);\n    }\n\n    public QMUICommonListItemView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUICommonListItemViewStyle);\n    }\n\n    public QMUICommonListItemView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    protected void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        LayoutInflater.from(context).inflate(R.layout.qmui_common_list_item, this, true);\n\n        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QMUICommonListItemView, defStyleAttr, 0);\n        @QMUICommonListItemOrientation int orientation = array.getInt(R.styleable.QMUICommonListItemView_qmui_orientation, HORIZONTAL);\n        @QMUICommonListItemAccessoryType int accessoryType = array.getInt(R.styleable.QMUICommonListItemView_qmui_accessory_type, ACCESSORY_TYPE_NONE);\n        final int initTitleColor = array.getColor(R.styleable.QMUICommonListItemView_qmui_common_list_title_color, 0);\n        final int initDetailColor = array.getColor(R.styleable.QMUICommonListItemView_qmui_common_list_detail_color, 0);\n        array.recycle();\n\n        mImageView = findViewById(R.id.group_list_item_imageView);\n        mTextView = findViewById(R.id.group_list_item_textView);\n        mRedDot = findViewById(R.id.group_list_item_tips_dot);\n        mNewTipView = findViewById(R.id.group_list_item_tips_new);\n        mDetailTextView = findViewById(R.id.group_list_item_detailTextView);\n        mTextView.setTextColor(initTitleColor);\n        mDetailTextView.setTextColor(initDetailColor);\n        mAccessoryView = findViewById(R.id.group_list_item_accessoryView);\n        setOrientation(orientation);\n        setAccessoryType(accessoryType);\n    }\n\n\n    public void updateImageViewLp(LayoutParamConfig lpConfig) {\n        if (lpConfig != null) {\n            LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();\n            mImageView.setLayoutParams(lpConfig.onConfig(lp));\n        }\n    }\n\n    public void setImageDrawable(Drawable drawable) {\n        if (drawable == null) {\n            mImageView.setVisibility(View.GONE);\n        } else {\n            mImageView.setImageDrawable(drawable);\n            mImageView.setVisibility(View.VISIBLE);\n        }\n    }\n\n    public void setTipPosition(@QMUICommonListItemTipPosition int tipPosition) {\n        if(mTipPosition != tipPosition){\n            mTipPosition = tipPosition;\n            updateLayoutParams();\n        }\n    }\n\n    public CharSequence getText() {\n        return mTextView.getText();\n    }\n\n    public void setText(CharSequence text) {\n        mTextView.setText(text);\n        if (QMUILangHelper.isNullOrEmpty(text)) {\n            mTextView.setVisibility(View.GONE);\n        } else {\n            mTextView.setVisibility(View.VISIBLE);\n        }\n    }\n\n    /**\n     * 切换是否显示小红点\n     *\n     * @param isShow 是否显示小红点\n     */\n    public void showRedDot(boolean isShow) {\n        int oldTipShown = mTipShown;\n        if(isShow){\n            mTipShown = TIP_SHOW_RED_POINT;\n        }else if(mTipShown == TIP_SHOW_RED_POINT){\n            mTipShown = TIP_SHOW_NOTHING;\n        }\n        if(oldTipShown != mTipShown){\n            updateLayoutParams();\n        }\n    }\n\n    /**\n     * 切换是否显示更新提示\n     *\n     * @param isShow 是否显示更新提示\n     */\n    public void showNewTip(boolean isShow) {\n        int oldTipShown = mTipShown;\n        if(isShow){\n            mTipShown = TIP_SHOW_NEW;\n        }else if(mTipShown == TIP_SHOW_NEW){\n            mTipShown = TIP_SHOW_NOTHING;\n        }\n        if(oldTipShown != mTipShown){\n            updateLayoutParams();\n        }\n    }\n\n\n    public CharSequence getDetailText() {\n        return mDetailTextView.getText();\n    }\n\n\n    public void setDetailText(CharSequence text) {\n        mDetailTextView.setText(text);\n        if (QMUILangHelper.isNullOrEmpty(text)) {\n            mDetailTextView.setVisibility(View.GONE);\n        } else {\n            mDetailTextView.setVisibility(View.VISIBLE);\n        }\n    }\n\n    public int getOrientation() {\n        return mOrientation;\n    }\n\n    public void setOrientation(@QMUICommonListItemOrientation int orientation) {\n        if (mOrientation == orientation) {\n            return;\n        }\n        mOrientation = orientation;\n        updateLayoutParams();\n    }\n\n    private void updateLayoutParams(){\n        mNewTipView.setVisibility(mTipShown == TIP_SHOW_NEW ? View.VISIBLE : View.GONE);\n        mRedDot.setVisibility(mTipShown == TIP_SHOW_RED_POINT ? View.VISIBLE : View.GONE);\n        LayoutParams titleLp = (LayoutParams) mTextView.getLayoutParams();\n        LayoutParams detailLp = (LayoutParams) mDetailTextView.getLayoutParams();\n        LayoutParams newTipLp = (LayoutParams) mNewTipView.getLayoutParams();\n        LayoutParams redDotLp = (LayoutParams) mRedDot.getLayoutParams();\n        if (mOrientation == VERTICAL) {\n            mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,\n                    QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_title_v_text_size));\n            mDetailTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,\n                    QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_detail_v_text_size));\n            titleLp.verticalChainStyle = LayoutParams.CHAIN_PACKED;\n            titleLp.bottomToBottom = LayoutParams.UNSET;\n            titleLp.bottomToTop = mDetailTextView.getId();\n\n            detailLp.horizontalChainStyle = LayoutParams.UNSET;\n            detailLp.verticalChainStyle = LayoutParams.CHAIN_PACKED;\n            detailLp.leftToLeft = mTextView.getId();\n            detailLp.leftToRight = LayoutParams.UNSET;\n            detailLp.horizontalBias = 0f;\n            detailLp.topToTop = LayoutParams.UNSET;\n            detailLp.topToBottom = mTextView.getId();\n            detailLp.leftMargin = 0;\n            detailLp.topMargin = QMUIResHelper.getAttrDimen(\n                    getContext(), R.attr.qmui_common_list_item_detail_v_margin_with_title);\n\n            if(mTipShown == TIP_SHOW_NEW){\n                if(mTipPosition == TIP_POSITION_LEFT){\n                    updateTipLeftVerRelatedLayoutParam(mNewTipView, newTipLp, titleLp, detailLp);\n                }else{\n                    updateTipRightVerRelatedLayoutParam(mNewTipView, newTipLp, titleLp, detailLp);\n                }\n            }else if(mTipShown == TIP_SHOW_RED_POINT){\n                if(mTipPosition == TIP_POSITION_LEFT){\n                    updateTipLeftVerRelatedLayoutParam(mRedDot, redDotLp, titleLp, detailLp);\n                }else{\n                    updateTipRightVerRelatedLayoutParam(mRedDot, redDotLp, titleLp, detailLp);\n                }\n            }else{\n                int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n                titleLp.horizontalChainStyle = LayoutParams.UNSET;\n                titleLp.rightToLeft = mAccessoryView.getId();\n                titleLp.rightMargin = accessoryLeftMargin;\n                titleLp.goneRightMargin = 0;\n                detailLp.leftToRight = mAccessoryView.getId();\n                detailLp.rightMargin = accessoryLeftMargin;\n                detailLp.goneRightMargin = 0;\n            }\n        } else {\n            mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,\n                    QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_title_h_text_size));\n            mDetailTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,\n                    QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_detail_h_text_size));\n            titleLp.verticalChainStyle = LayoutParams.UNSET;\n            titleLp.bottomToBottom = LayoutParams.PARENT_ID;\n            titleLp.bottomToTop = LayoutParams.UNSET;\n\n            detailLp.verticalChainStyle = LayoutParams.UNSET;\n            detailLp.leftToLeft = LayoutParams.UNSET;\n            detailLp.topToTop = LayoutParams.PARENT_ID;\n            detailLp.topToBottom = LayoutParams.UNSET;\n            detailLp.topMargin = 0;\n            detailLp.leftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_detail_h_margin_with_title);\n\n            if(mTipShown == TIP_SHOW_NEW){\n                if(mTipPosition == TIP_POSITION_LEFT){\n                    updateTipLeftHorRelatedLayoutParam(mNewTipView, newTipLp, titleLp, detailLp);\n                }else{\n                    updateTipRightHorRelatedLayoutParam(mNewTipView, newTipLp, titleLp, detailLp);\n                }\n            }else if(mTipShown == TIP_SHOW_RED_POINT){\n                if(mTipPosition == TIP_POSITION_LEFT){\n                    updateTipLeftHorRelatedLayoutParam(mRedDot, redDotLp, titleLp, detailLp);\n                }else{\n                    updateTipRightHorRelatedLayoutParam(mRedDot, redDotLp, titleLp, detailLp);\n                }\n            }else{\n                int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n                titleLp.horizontalChainStyle = LayoutParams.UNSET;\n                titleLp.rightToLeft = mAccessoryView.getId();\n                titleLp.rightMargin = accessoryLeftMargin;\n                titleLp.goneRightMargin = 0;\n                detailLp.leftToRight = mTextView.getId();\n                detailLp.rightToLeft = mAccessoryView.getId();\n                detailLp.rightMargin = accessoryLeftMargin;\n                detailLp.goneRightMargin = 0;\n            }\n        }\n        mTextView.setLayoutParams(titleLp);\n        mDetailTextView.setLayoutParams(detailLp);\n        mNewTipView.setLayoutParams(newTipLp);\n        mRedDot.setLayoutParams(redDotLp);\n    }\n\n    private void updateTipLeftVerRelatedLayoutParam(View tipView,\n                                                    ConstraintLayout.LayoutParams tipLp,\n                                                    ConstraintLayout.LayoutParams titleLp,\n                                                    ConstraintLayout.LayoutParams detailLp){\n        int titleRightMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_holder_margin_with_title);\n        int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n        titleLp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        titleLp.horizontalBias = 0f;\n        titleLp.rightToLeft = tipView.getId();\n        titleLp.rightMargin = titleRightMargin;\n        tipLp.leftToRight = mTextView.getId();\n        tipLp.rightToLeft = mAccessoryView.getId();\n        tipLp.rightMargin = accessoryLeftMargin;\n        tipLp.topToTop = mTextView.getId();\n        tipLp.bottomToBottom = mTextView.getId();\n        tipLp.goneRightMargin = 0;\n        detailLp.rightToLeft = mAccessoryView.getId();\n        detailLp.rightMargin = accessoryLeftMargin;\n        detailLp.goneRightMargin = 0;\n    }\n\n    private void updateTipRightVerRelatedLayoutParam(View tipView,\n                                                     ConstraintLayout.LayoutParams tipLp,\n                                                     ConstraintLayout.LayoutParams titleLp,\n                                                     ConstraintLayout.LayoutParams detailLp){\n        int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n\n        tipLp.leftToRight = LayoutParams.UNSET;\n        tipLp.rightToLeft = mAccessoryView.getId();\n        tipLp.rightMargin = accessoryLeftMargin;\n        tipLp.goneRightMargin = 0;\n        tipLp.topToTop = LayoutParams.PARENT_ID;\n        tipLp.bottomToBottom = LayoutParams.PARENT_ID;\n\n        titleLp.horizontalChainStyle = LayoutParams.UNSET;\n        titleLp.rightToLeft = tipView.getId();\n        titleLp.rightMargin = accessoryLeftMargin;\n\n        detailLp.rightToLeft = tipView.getId();\n        detailLp.rightMargin = accessoryLeftMargin;\n    }\n\n    private void updateTipLeftHorRelatedLayoutParam(View tipView,\n                                                    ConstraintLayout.LayoutParams tipLp,\n                                                    ConstraintLayout.LayoutParams titleLp,\n                                                    ConstraintLayout.LayoutParams detailLp){\n        int titleRightMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_holder_margin_with_title);\n        int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n        titleLp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        titleLp.horizontalBias = 0f;\n        titleLp.rightToLeft = tipView.getId();\n        titleLp.rightMargin = titleRightMargin;\n        tipLp.leftToRight = mTextView.getId();\n        tipLp.rightToLeft = mAccessoryView.getId();\n        tipLp.rightMargin = accessoryLeftMargin;\n        tipLp.topToTop = mTextView.getId();\n        tipLp.bottomToBottom = mTextView.getId();\n        tipLp.goneRightMargin = 0;\n        detailLp.leftToRight = tipView.getId();\n        detailLp.rightToLeft = mAccessoryView.getId();\n        detailLp.rightMargin = accessoryLeftMargin;\n        detailLp.goneRightMargin = 0;\n    }\n\n    private void updateTipRightHorRelatedLayoutParam(View tipView,\n                                                     ConstraintLayout.LayoutParams tipLp,\n                                                     ConstraintLayout.LayoutParams titleLp,\n                                                     ConstraintLayout.LayoutParams detailLp){\n        int accessoryLeftMargin = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_common_list_item_accessory_margin_left);\n\n        tipLp.leftToRight = LayoutParams.UNSET;\n        tipLp.rightToLeft = mAccessoryView.getId();\n        tipLp.rightMargin = accessoryLeftMargin;\n        tipLp.goneRightMargin = 0;\n        tipLp.topToTop = LayoutParams.PARENT_ID;\n        tipLp.bottomToBottom = LayoutParams.PARENT_ID;\n\n        titleLp.horizontalChainStyle = LayoutParams.UNSET;\n        titleLp.rightToLeft = tipView.getId();\n        titleLp.rightMargin = accessoryLeftMargin;\n        titleLp.horizontalBias = 0f;\n\n        detailLp.leftToRight = mTextView.getId();\n        detailLp.rightToLeft = tipView.getId();\n        detailLp.rightMargin = accessoryLeftMargin;\n    }\n\n    public int getAccessoryType() {\n        return mAccessoryType;\n    }\n\n    /**\n     * 设置右侧 View 的类型。\n     *\n     * @param type 见 {@link QMUICommonListItemAccessoryType}\n     */\n    public void setAccessoryType(@QMUICommonListItemAccessoryType int type) {\n        mAccessoryView.removeAllViews();\n        mAccessoryType = type;\n\n        switch (type) {\n            // 向右的箭头\n            case ACCESSORY_TYPE_CHEVRON: {\n                ImageView tempImageView = getAccessoryImageView();\n                tempImageView.setImageDrawable(QMUIResHelper.getAttrDrawable(getContext(), R.attr.qmui_common_list_item_chevron));\n                mAccessoryView.addView(tempImageView);\n                mAccessoryView.setVisibility(VISIBLE);\n            }\n            break;\n            // switch开关\n            case ACCESSORY_TYPE_SWITCH: {\n                if (mSwitch == null) {\n                    mSwitch = new AppCompatCheckBox(getContext());\n                    mSwitch.setBackground(null);\n                    mSwitch.setButtonDrawable(QMUIResHelper.getAttrDrawable(getContext(), R.attr.qmui_common_list_item_switch));\n                    mSwitch.setLayoutParams(getAccessoryLayoutParams());\n                    if(mDisableSwitchSelf){\n                        mSwitch.setClickable(false);\n                        mSwitch.setEnabled(false);\n                    }\n                }\n                mAccessoryView.addView(mSwitch);\n                mAccessoryView.setVisibility(VISIBLE);\n            }\n            break;\n            // 自定义View\n            case ACCESSORY_TYPE_CUSTOM:\n                mAccessoryView.setVisibility(VISIBLE);\n                break;\n            // 清空所有accessoryView\n            case ACCESSORY_TYPE_NONE:\n                mAccessoryView.setVisibility(GONE);\n                break;\n        }\n        LayoutParams titleLp = (LayoutParams) mTextView.getLayoutParams();\n        LayoutParams detailLp = (LayoutParams) mDetailTextView.getLayoutParams();\n        if (mAccessoryView.getVisibility() != View.GONE) {\n            detailLp.goneRightMargin = detailLp.rightMargin;\n            titleLp.goneRightMargin = titleLp.rightMargin;\n        } else {\n            detailLp.goneRightMargin = 0;\n            titleLp.goneRightMargin = 0;\n        }\n    }\n\n    private ViewGroup.LayoutParams getAccessoryLayoutParams() {\n        return new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    private ImageView getAccessoryImageView() {\n        AppCompatImageView resultImageView = new AppCompatImageView(getContext());\n        resultImageView.setLayoutParams(getAccessoryLayoutParams());\n        resultImageView.setScaleType(ImageView.ScaleType.CENTER);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.tintColor(R.attr.qmui_skin_support_common_list_chevron_color);\n        QMUISkinHelper.setSkinValue(resultImageView, builder);\n        QMUISkinValueBuilder.release(builder);\n        return resultImageView;\n    }\n\n    public TextView getTextView() {\n        return mTextView;\n    }\n\n    public TextView getDetailTextView() {\n        return mDetailTextView;\n    }\n\n    public CheckBox getSwitch() {\n        return mSwitch;\n    }\n\n    public ViewGroup getAccessoryContainerView() {\n        return mAccessoryView;\n    }\n\n    /**\n     * 添加自定义的 Accessory View\n     *\n     * @param view 自定义的 Accessory View\n     */\n    public void addAccessoryCustomView(View view) {\n        if (mAccessoryType == ACCESSORY_TYPE_CUSTOM) {\n            mAccessoryView.addView(view);\n        }\n    }\n\n    public void setDisableSwitchSelf(boolean disableSwitchSelf) {\n        mDisableSwitchSelf = disableSwitchSelf;\n        if(mSwitch != null){\n            mSwitch.setClickable(!disableSwitchSelf);\n            mSwitch.setEnabled(!disableSwitchSelf);\n        }\n    }\n\n    public void setSkinConfig(SkinConfig skinConfig) {\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        if (skinConfig.iconTintColorRes != 0) {\n            builder.tintColor(skinConfig.iconTintColorRes);\n        }\n        if (skinConfig.iconSrcRes != 0) {\n            builder.src(skinConfig.iconSrcRes);\n        }\n        QMUISkinHelper.setSkinValue(mImageView, builder);\n\n        builder.clear();\n        if (skinConfig.titleTextColorRes != 0) {\n            builder.textColor(skinConfig.titleTextColorRes);\n        }\n        QMUISkinHelper.setSkinValue(mTextView, builder);\n\n        builder.clear();\n        if (skinConfig.detailTextColorRes != 0) {\n            builder.textColor(skinConfig.detailTextColorRes);\n        }\n        QMUISkinHelper.setSkinValue(mDetailTextView, builder);\n\n        builder.clear();\n        if (skinConfig.newTipSrcRes != 0) {\n            builder.src(skinConfig.newTipSrcRes);\n        }\n        QMUISkinHelper.setSkinValue(mNewTipView, builder);\n\n        builder.clear();\n        if (skinConfig.tipDotColorRes != 0) {\n            builder.bgTintColor(skinConfig.tipDotColorRes);\n        }\n        QMUISkinHelper.setSkinValue(mRedDot, builder);\n        builder.release();\n    }\n\n\n    public interface LayoutParamConfig {\n        ConstraintLayout.LayoutParams onConfig(ConstraintLayout.LayoutParams lp);\n    }\n\n    public static class SkinConfig {\n\n        public int iconTintColorRes = R.attr.qmui_skin_support_common_list_icon_tint_color;\n        public int iconSrcRes = 0;\n        public int titleTextColorRes = R.attr.qmui_skin_support_common_list_title_color;\n        public int detailTextColorRes = R.attr.qmui_skin_support_common_list_detail_color;\n        public int newTipSrcRes = R.attr.qmui_skin_support_common_list_new_drawable;\n        public int tipDotColorRes = R.attr.qmui_skin_support_common_list_red_point_tint_color;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/grouplist/QMUIGroupListSectionHeaderFooterView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.grouplist;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\n\n/**\n * 用作通用列表 {@link QMUIGroupListView} 里每个 {@link QMUIGroupListView.Section} 的头部或尾部，也可单独使用。\n *\n * @author molicechen\n * @date 2015-01-07\n */\n\npublic class QMUIGroupListSectionHeaderFooterView extends LinearLayout {\n\n    private TextView mTextView;\n\n    public QMUIGroupListSectionHeaderFooterView(Context context) {\n        this(context, null, R.attr.QMUIGroupListSectionViewStyle);\n    }\n\n\n    public QMUIGroupListSectionHeaderFooterView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIGroupListSectionViewStyle);\n    }\n\n    public QMUIGroupListSectionHeaderFooterView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    public QMUIGroupListSectionHeaderFooterView(Context context, CharSequence titleText) {\n        this(context);\n        setText(titleText);\n    }\n\n    public QMUIGroupListSectionHeaderFooterView(Context context, CharSequence titleText, boolean isFooter) {\n        this(context);\n\n        if (isFooter) {\n            // Footer View 不需要 padding bottom\n            setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), 0);\n        }\n\n        setText(titleText);\n    }\n\n    private void init(Context context) {\n        LayoutInflater.from(context).inflate(R.layout.qmui_group_list_section_layout, this, true);\n        setGravity(Gravity.BOTTOM);\n\n        mTextView = (TextView) findViewById(R.id.group_list_section_header_textView);\n    }\n\n    public void setText(CharSequence text) {\n        if (QMUILangHelper.isNullOrEmpty(text)) {\n            mTextView.setVisibility(GONE);\n        } else {\n            mTextView.setVisibility(VISIBLE);\n        }\n        mTextView.setText(text);\n    }\n\n    public TextView getTextView() {\n        return mTextView;\n    }\n\n    public void setTextGravity(int gravity) {\n        mTextView.setGravity(gravity);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/grouplist/QMUIGroupListView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.grouplist;\n\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\nimport androidx.annotation.Nullable;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\n/**\n * 通用的列表, 常用于 App 的设置界面。\n * <p>\n * 注意其父类不是 {@link android.widget.ListView}, 而是 {@link LinearLayout}, 一般需要在外层包多一个 {@link android.widget.ScrollView} 来支持滚动。\n * </p>\n * <p>\n * 提供了 {@link Section} 的概念, 用来将列表分块。 具体见 {@link QMUIGroupListView.Section}\n * </p>\n *\n * @author cginechen\n * @date 2016-10-13\n */\n\npublic class QMUIGroupListView extends LinearLayout {\n\n\n    private SparseArray<Section> mSections;\n\n    public QMUIGroupListView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIGroupListView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIGroupListView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        mSections = new SparseArray<>();\n        setOrientation(LinearLayout.VERTICAL);\n    }\n\n    /**\n     * 创建一个 Section。\n     *\n     * @return 返回新创建的 Section。\n     */\n    public static Section newSection(Context context) {\n        return new Section(context);\n    }\n\n\n    public int getSectionCount() {\n        return mSections.size();\n    }\n\n    public QMUICommonListItemView createItemView(@Nullable Drawable imageDrawable, CharSequence titleText, String detailText, int orientation, int accessoryType, int height) {\n        QMUICommonListItemView itemView = new QMUICommonListItemView(getContext());\n        itemView.setOrientation(orientation);\n        itemView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));\n        itemView.setImageDrawable(imageDrawable);\n        itemView.setText(titleText);\n        itemView.setDetailText(detailText);\n        itemView.setAccessoryType(accessoryType);\n        return itemView;\n    }\n\n    public QMUICommonListItemView createItemView(@Nullable Drawable imageDrawable, CharSequence titleText, String detailText, int orientation, int accessoryType) {\n        int height;\n        if (orientation == QMUICommonListItemView.VERTICAL) {\n            height = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_list_item_height_higher);\n            return createItemView(imageDrawable, titleText, detailText, orientation, accessoryType, height);\n        } else {\n            height = QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_list_item_height);\n            return createItemView(imageDrawable, titleText, detailText, orientation, accessoryType, height);\n        }\n    }\n\n    public QMUICommonListItemView createItemView(CharSequence titleText) {\n        return createItemView(null, titleText, null, QMUICommonListItemView.HORIZONTAL, QMUICommonListItemView.ACCESSORY_TYPE_NONE);\n    }\n\n    public QMUICommonListItemView createItemView(int orientation) {\n        return createItemView(null, null, null, orientation, QMUICommonListItemView.ACCESSORY_TYPE_NONE);\n    }\n\n    /**\n     * private, use {@link Section#addTo(QMUIGroupListView)}\n     * <p>这里只是把section记录到数组里面而已</p>\n     */\n    private void addSection(Section section) {\n        mSections.append(mSections.size(), section);\n    }\n\n    /**\n     * private，use {@link Section#removeFrom(QMUIGroupListView)}\n     * <p>这里只是把section从记录的数组里移除而已</p>\n     */\n    private void removeSection(Section section) {\n        for (int i = 0; i < mSections.size(); i++) {\n            Section each = mSections.valueAt(i);\n            if (each == section) {\n                mSections.remove(i);\n            }\n        }\n    }\n\n    public Section getSection(int index) {\n        return mSections.get(index);\n    }\n\n\n    /**\n     * Section 是组成 {@link QMUIGroupListView} 的部分。\n     * <ul>\n     * <li>每个 Section 可以有多个 item, 通过 {@link #addItemView(QMUICommonListItemView, OnClickListener)} 添加。</li>\n     * <li>Section 还可以有自己的一个顶部 title 和一个底部 description, 通过 {@link #setTitle(CharSequence)} 和 {@link #setDescription(CharSequence)} 设置。</li>\n     * </ul>\n     */\n    public static class Section {\n        private Context mContext;\n        private QMUIGroupListSectionHeaderFooterView mTitleView;\n        private QMUIGroupListSectionHeaderFooterView mDescriptionView;\n        private SparseArray<QMUICommonListItemView> mItemViews;\n        private boolean mUseDefaultTitleIfNone;\n        private boolean mUseTitleViewForSectionSpace = true;\n        private int mSeparatorColorAttr = R.attr.qmui_skin_support_common_list_separator_color;\n        private boolean mHandleSeparatorCustom = false;\n        private boolean mShowSeparator = true;\n        private boolean mOnlyShowStartEndSeparator = false;\n        private boolean mOnlyShowMiddleSeparator = false;\n        private int mMiddleSeparatorInsetLeft = 0;\n        private int mMiddleSeparatorInsetRight = 0;\n        private int mBgAttr = R.attr.qmui_skin_support_s_common_list_bg;\n\n        private int mLeftIconWidth = ViewGroup.LayoutParams.WRAP_CONTENT;\n        private int mLeftIconHeight = ViewGroup.LayoutParams.WRAP_CONTENT;\n\n        public Section(Context context) {\n            mContext = context;\n            mItemViews = new SparseArray<>();\n        }\n\n        /**\n         * 对 Section 添加一个 {@link QMUICommonListItemView}\n         *\n         * @param itemView        要添加的 ItemView\n         * @param onClickListener ItemView 的点击事件\n         * @return Section 本身,支持链式调用\n         */\n        public Section addItemView(QMUICommonListItemView itemView, OnClickListener onClickListener) {\n            return addItemView(itemView, onClickListener, null);\n        }\n\n        /**\n         * 对 Section 添加一个 {@link QMUICommonListItemView}\n         *\n         * @param itemView            要添加的 ItemView\n         * @param onClickListener     ItemView 的点击事件\n         * @param onLongClickListener ItemView 的长按事件\n         * @return Section 本身, 支持链式调用\n         */\n        public Section addItemView(final QMUICommonListItemView itemView, OnClickListener onClickListener, OnLongClickListener onLongClickListener) {\n            if (onClickListener != null) {\n                itemView.setOnClickListener(onClickListener);\n            }\n\n            if (onLongClickListener != null) {\n                itemView.setOnLongClickListener(onLongClickListener);\n            }\n\n            mItemViews.append(mItemViews.size(), itemView);\n            return this;\n        }\n\n        /**\n         * 设置 Section 的 title\n         *\n         * @return Section 本身, 支持链式调用\n         */\n        public Section setTitle(CharSequence title) {\n            mTitleView = createSectionHeader(title);\n            return this;\n        }\n\n        /**\n         * 设置 Section 的 description\n         *\n         * @return Section 本身, 支持链式调用\n         */\n        public Section setDescription(CharSequence description) {\n            mDescriptionView = createSectionFooter(description);\n            return this;\n        }\n\n        public Section setUseDefaultTitleIfNone(boolean useDefaultTitleIfNone) {\n            mUseDefaultTitleIfNone = useDefaultTitleIfNone;\n            return this;\n        }\n\n        public Section setUseTitleViewForSectionSpace(boolean useTitleViewForSectionSpace) {\n            mUseTitleViewForSectionSpace = useTitleViewForSectionSpace;\n            return this;\n        }\n\n        public Section setLeftIconSize(int width, int height) {\n            mLeftIconHeight = height;\n            mLeftIconWidth = width;\n            return this;\n        }\n\n        public Section setSeparatorColorAttr(int attr) {\n            mSeparatorColorAttr = attr;\n            return this;\n        }\n\n        public Section setHandleSeparatorCustom(boolean handleSeparatorCustom) {\n            mHandleSeparatorCustom = handleSeparatorCustom;\n            return this;\n        }\n\n        public Section setShowSeparator(boolean showSeparator) {\n            mShowSeparator = showSeparator;\n            return this;\n        }\n\n        public Section setOnlyShowStartEndSeparator(boolean onlyShowStartEndSeparator) {\n            mOnlyShowStartEndSeparator = onlyShowStartEndSeparator;\n            return this;\n        }\n\n        public Section setOnlyShowMiddleSeparator(boolean onlyShowMiddleSeparator) {\n            mOnlyShowMiddleSeparator = onlyShowMiddleSeparator;\n            return this;\n        }\n\n        public Section setMiddleSeparatorInset(int insetLeft, int insetRight) {\n            mMiddleSeparatorInsetLeft = insetLeft;\n            mMiddleSeparatorInsetRight = insetRight;\n            return this;\n        }\n\n        public Section setBgAttr(int bgAttr) {\n            mBgAttr = bgAttr;\n            return this;\n        }\n\n\n        /**\n         * 将 Section 添加到 {@link QMUIGroupListView} 上\n         */\n        public void addTo(QMUIGroupListView groupListView) {\n            if (mTitleView == null) {\n                if (mUseDefaultTitleIfNone) {\n                    setTitle(\"Section \" + groupListView.getSectionCount());\n                } else if (mUseTitleViewForSectionSpace) {\n                    setTitle(\"\");\n                }\n            }\n            if (mTitleView != null) {\n                groupListView.addView(mTitleView);\n            }\n\n\n            final int itemViewCount = mItemViews.size();\n            QMUICommonListItemView.LayoutParamConfig leftIconLpConfig = new QMUICommonListItemView.LayoutParamConfig() {\n                @Override\n                public ConstraintLayout.LayoutParams onConfig(ConstraintLayout.LayoutParams lp) {\n                    lp.width = mLeftIconWidth;\n                    lp.height = mLeftIconHeight;\n                    return lp;\n                }\n            };\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            String skin = builder.background(mBgAttr)\n                    .topSeparator(mSeparatorColorAttr)\n                    .bottomSeparator(mSeparatorColorAttr)\n                    .build();\n            QMUISkinValueBuilder.release(builder);\n            int separatorColor = QMUIResHelper.getAttrColor(groupListView.getContext(), mSeparatorColorAttr);\n            for (int i = 0; i < itemViewCount; i++) {\n                QMUICommonListItemView itemView = mItemViews.get(i);\n                Drawable bg = QMUISkinHelper.getSkinDrawable(groupListView, mBgAttr);\n                QMUIViewHelper.setBackgroundKeepingPadding(itemView, bg == null ? null : bg.mutate());\n                QMUISkinHelper.setSkinValue(itemView, skin);\n                if (!mHandleSeparatorCustom && mShowSeparator) {\n                    if (itemViewCount == 1) {\n                        itemView.updateTopDivider(0, 0, 1, separatorColor);\n                        itemView.updateBottomDivider(0, 0, 1, separatorColor);\n                    } else if (i == 0) {\n                        if(!mOnlyShowMiddleSeparator){\n                            itemView.updateTopDivider(0, 0, 1, separatorColor);\n                        }\n                        if (!mOnlyShowStartEndSeparator) {\n                            itemView.updateBottomDivider(\n                                    mMiddleSeparatorInsetLeft, mMiddleSeparatorInsetRight, 1, separatorColor);\n                        }\n                    } else if (i == itemViewCount - 1) {\n                        if(!mOnlyShowMiddleSeparator){\n                            itemView.updateBottomDivider(0, 0, 1, separatorColor);\n                        }\n                    } else if (!mOnlyShowStartEndSeparator) {\n                        itemView.updateBottomDivider(mMiddleSeparatorInsetLeft, mMiddleSeparatorInsetRight, 1, separatorColor);\n                    }\n                }\n                itemView.updateImageViewLp(leftIconLpConfig);\n                groupListView.addView(itemView);\n            }\n\n            if (mDescriptionView != null) {\n                groupListView.addView(mDescriptionView);\n            }\n            groupListView.addSection(this);\n        }\n\n        public void removeFrom(QMUIGroupListView parent) {\n            if (mTitleView != null && mTitleView.getParent() == parent) {\n                parent.removeView(mTitleView);\n            }\n            for (int i = 0; i < mItemViews.size(); i++) {\n                QMUICommonListItemView itemView = mItemViews.get(i);\n                parent.removeView(itemView);\n            }\n            if (mDescriptionView != null && mDescriptionView.getParent() == parent) {\n                parent.removeView(mDescriptionView);\n            }\n            parent.removeSection(this);\n        }\n\n        /**\n         * 创建 Section Header，每个 Section 都会被创建一个 Header，有 title 时会显示 title，没有 title 时会利用 header 的上下 padding 充当 Section 分隔条\n         */\n        public QMUIGroupListSectionHeaderFooterView createSectionHeader(CharSequence titleText) {\n            return new QMUIGroupListSectionHeaderFooterView(mContext, titleText);\n        }\n\n        /**\n         * Section 的 Footer，形式与 Header 相似，都是显示一段文字\n         */\n        public QMUIGroupListSectionHeaderFooterView createSectionFooter(CharSequence text) {\n            return new QMUIGroupListSectionHeaderFooterView(mContext, text, true);\n        }\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUIBasePopup.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Color;\nimport android.graphics.drawable.ColorDrawable;\nimport android.os.Build;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.PopupWindow;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport java.lang.ref.WeakReference;\n\npublic abstract class QMUIBasePopup<T extends QMUIBasePopup> {\n    public static final float DIM_AMOUNT_NOT_EXIST = -1f;\n    public static final int NOT_SET = -1;\n\n    protected final PopupWindow mWindow;\n    protected WindowManager mWindowManager;\n    protected Context mContext;\n    protected WeakReference<View> mAttachedViewRf;\n    private float mDimAmount = DIM_AMOUNT_NOT_EXIST;\n    private int mDimAmountAttr = 0;\n    private PopupWindow.OnDismissListener mDismissListener;\n    private QMUISkinManager mSkinManager;\n    private QMUISkinManager.OnSkinChangeListener mOnSkinChangeListener = new QMUISkinManager.OnSkinChangeListener() {\n        @Override\n        public void onSkinChange(QMUISkinManager skinManager, int oldSkin, int newSkin) {\n            if (mDimAmountAttr != 0) {\n                Resources.Theme theme = skinManager.getTheme(newSkin);\n                mDimAmount = QMUIResHelper.getAttrFloatValue(theme, mDimAmountAttr);\n                updateDimAmount(mDimAmount);\n                QMUIBasePopup.this.onSkinChange(oldSkin, newSkin);\n            }\n        }\n    };\n\n    private View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {\n        @Override\n        public void onViewAttachedToWindow(View v) {\n\n        }\n\n        @Override\n        public void onViewDetachedFromWindow(View v) {\n            dismiss();\n        }\n    };\n    private View.OnTouchListener mOutsideTouchDismissListener = new View.OnTouchListener() {\n        @Override\n        public boolean onTouch(View v, MotionEvent event) {\n            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {\n                mWindow.dismiss();\n                return true;\n            }\n            return false;\n        }\n    };\n\n\n\n    public QMUIBasePopup(Context context) {\n        mContext = context;\n        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        mWindow = new PopupWindow(context);\n        mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));\n        mWindow.setFocusable(true);\n        mWindow.setTouchable(true);\n        mWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {\n            @Override\n            public void onDismiss() {\n                removeOldAttachStateChangeListener();\n                mAttachedViewRf = null;\n                if(mSkinManager != null){\n                    mSkinManager.unRegister(mWindow);\n                    mSkinManager.removeSkinChangeListener(mOnSkinChangeListener);\n                }\n                QMUIBasePopup.this.onDismiss();\n                if (mDismissListener != null) {\n                    mDismissListener.onDismiss();\n                }\n            }\n        });\n        dismissIfOutsideTouch(true);\n    }\n\n    protected void onSkinChange(int oldSkin, int newSkin){\n\n    }\n\n    public QMUISkinManager getSkinManager() {\n        return mSkinManager;\n    }\n\n    public T dimAmount(float dimAmount) {\n        mDimAmount = dimAmount;\n        return (T) this;\n    }\n\n    public T dimAmountAttr(int dimAmountAttr) {\n        mDimAmountAttr = dimAmountAttr;\n        return (T) this;\n    }\n\n    public T skinManager(@Nullable QMUISkinManager skinManager) {\n        mSkinManager = skinManager;\n        return (T) this;\n    }\n\n    public T setTouchable(boolean touchable){\n        mWindow.setTouchable(true);\n        return (T) this;\n    }\n\n    public T  setFocusable(boolean focusable){\n        mWindow.setFocusable(focusable);\n        return (T) this;\n    }\n\n    public T dismissIfOutsideTouch(boolean dismissIfOutsideTouch) {\n        mWindow.setOutsideTouchable(dismissIfOutsideTouch);\n        if (dismissIfOutsideTouch) {\n            mWindow.setTouchInterceptor(mOutsideTouchDismissListener);\n        } else {\n            mWindow.setTouchInterceptor(null);\n        }\n        return (T) this;\n    }\n\n    public T onDismiss(PopupWindow.OnDismissListener listener) {\n        mDismissListener = listener;\n        return (T) this;\n    }\n\n    private void removeOldAttachStateChangeListener() {\n        if (mAttachedViewRf != null) {\n            View oldAttachedView = mAttachedViewRf.get();\n            if (oldAttachedView != null) {\n                oldAttachedView.removeOnAttachStateChangeListener(mOnAttachStateChangeListener);\n            }\n        }\n    }\n\n    public View getDecorView() {\n        View decorView = null;\n        try {\n            if (mWindow.getBackground() == null) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                    decorView = (View) mWindow.getContentView().getParent();\n                } else {\n                    decorView = mWindow.getContentView();\n                }\n            } else {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                    decorView = (View) mWindow.getContentView().getParent().getParent();\n                } else {\n                    decorView = (View) mWindow.getContentView().getParent();\n                }\n            }\n        } catch (Exception ignore) {\n\n        }\n\n        return decorView;\n    }\n\n    protected void showAtLocation(@NonNull View parent, int x, int y) {\n        if (!ViewCompat.isAttachedToWindow(parent)) {\n            return;\n        }\n        removeOldAttachStateChangeListener();\n        parent.addOnAttachStateChangeListener(mOnAttachStateChangeListener);\n        mAttachedViewRf = new WeakReference<>(parent);\n        mWindow.showAtLocation(parent, Gravity.NO_GRAVITY, x, y);\n        if (mSkinManager != null) {\n            mSkinManager.register(mWindow);\n            mSkinManager.addSkinChangeListener(mOnSkinChangeListener);\n            if (mDimAmountAttr != 0) {\n                Resources.Theme currentTheme = mSkinManager.getCurrentTheme();\n                currentTheme = currentTheme == null ? parent.getContext().getTheme() : currentTheme;\n                mDimAmount = QMUIResHelper.getAttrFloatValue(currentTheme, mDimAmountAttr);\n            }\n        }\n        if (mDimAmount != DIM_AMOUNT_NOT_EXIST) {\n            updateDimAmount(mDimAmount);\n        }\n    }\n\n    private void updateDimAmount(float dimAmount) {\n        View decorView = getDecorView();\n        if (decorView != null) {\n            WindowManager.LayoutParams p = (WindowManager.LayoutParams) decorView.getLayoutParams();\n            p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;\n            p.dimAmount = dimAmount;\n            modifyWindowLayoutParams(p);\n            mWindowManager.updateViewLayout(decorView, p);\n        }\n    }\n\n    protected void modifyWindowLayoutParams(WindowManager.LayoutParams lp) {\n\n    }\n\n    protected void onDismiss() {\n\n    }\n\n    public final void dismiss() {\n        mWindow.dismiss();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUIFullScreenPopup.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\n\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaImageButton;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.IBlankTouchDetector;\n\nimport java.util.ArrayList;\n\nimport static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;\nimport static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;\n\npublic class QMUIFullScreenPopup extends QMUIBasePopup<QMUIFullScreenPopup> {\n    private OnBlankClickListener mOnBlankClickListener;\n    private boolean mAddCloseBtn = false;\n    private int mCloseIconAttr = R.attr.qmui_skin_support_popup_close_icon;\n    private Drawable mCloseIcon = null;\n    private ConstraintLayout.LayoutParams mCloseIvLayoutParams;\n    private int mAnimStyle = NOT_SET;\n    private ArrayList<ViewInfo> mViews = new ArrayList<>();\n\n    public QMUIFullScreenPopup(Context context) {\n        super(context);\n        mWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);\n        mWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);\n        mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);\n        dimAmount(0.6f);\n    }\n\n    public QMUIFullScreenPopup onBlankClick(OnBlankClickListener onBlankClickListener) {\n        mOnBlankClickListener = onBlankClickListener;\n        return this;\n    }\n\n    public QMUIFullScreenPopup closeBtn(boolean close) {\n        mAddCloseBtn = close;\n        return this;\n    }\n\n    public QMUIFullScreenPopup closeIcon(Drawable drawable) {\n        mCloseIcon = drawable;\n        return this;\n    }\n\n    public QMUIFullScreenPopup closeIconAttr(int closeIconAttr) {\n        mCloseIconAttr = closeIconAttr;\n        return this;\n    }\n\n    public QMUIFullScreenPopup closeLp(ConstraintLayout.LayoutParams contentLayoutParams) {\n        mCloseIvLayoutParams = contentLayoutParams;\n        return this;\n    }\n\n    public int getCloseBtnId() {\n        return R.id.qmui_popup_close_btn_id;\n    }\n\n    public QMUIFullScreenPopup animStyle(int animStyle) {\n        mAnimStyle = animStyle;\n        return this;\n    }\n\n    public QMUIFullScreenPopup addView(View view, ConstraintLayout.LayoutParams lp) {\n        mViews.add(new ViewInfo(view, lp));\n        return this;\n    }\n\n\n    public QMUIFullScreenPopup addView(View view) {\n        mViews.add(new ViewInfo(view, defaultContentLp()));\n        return this;\n    }\n\n    private ConstraintLayout.LayoutParams defaultContentLp() {\n        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(\n                ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n        return lp;\n    }\n\n    private ConstraintLayout.LayoutParams defaultCloseIvLp() {\n        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(\n                ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n        lp.bottomMargin = QMUIDisplayHelper.dp2px(mContext, 48);\n        return lp;\n    }\n\n    private QMUIAlphaImageButton createCloseIv() {\n        QMUIAlphaImageButton closeBtn = new QMUIAlphaImageButton(mContext);\n        closeBtn.setPadding(0, 0, 0, 0);\n        closeBtn.setScaleType(ImageView.ScaleType.CENTER);\n        closeBtn.setId(R.id.qmui_popup_close_btn_id);\n        closeBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                dismiss();\n            }\n        });\n        closeBtn.setFitsSystemWindows(true);\n        Drawable drawable = null;\n        if (mCloseIcon != null) {\n            drawable = mCloseIcon;\n        } else if (mCloseIconAttr != 0) {\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire().src(mCloseIconAttr);\n            QMUISkinHelper.setSkinValue(closeBtn, builder);\n            builder.release();\n            drawable = QMUIResHelper.getAttrDrawable(mContext, mCloseIconAttr);\n        }\n        closeBtn.setImageDrawable(drawable);\n        return closeBtn;\n    }\n\n    public boolean isShowing() {\n        return mWindow.isShowing();\n    }\n\n    public void show(View parent) {\n        if (isShowing()) {\n            return;\n        }\n        if (mViews.isEmpty()) {\n            throw new RuntimeException(\"you should call addView() to add content view\");\n        }\n        ArrayList<ViewInfo> views = new ArrayList<>(mViews);\n        RootView rootView = new RootView(mContext);\n        for (int i = 0; i < views.size(); i++) {\n            ViewInfo info = mViews.get(i);\n            View view = info.view;\n            if (view.getParent() != null) {\n                ((ViewGroup) view.getParent()).removeView(view);\n            }\n\n            rootView.addView(view, info.lp);\n        }\n        if (mAddCloseBtn) {\n            if (mCloseIvLayoutParams == null) {\n                mCloseIvLayoutParams = defaultCloseIvLp();\n            }\n            rootView.addView(createCloseIv(), mCloseIvLayoutParams);\n        }\n        mWindow.setContentView(rootView);\n        if (mAnimStyle != NOT_SET) {\n            mWindow.setAnimationStyle(mAnimStyle);\n        }\n\n        showAtLocation(parent, 0, 0);\n    }\n\n    @Override\n    protected void modifyWindowLayoutParams(WindowManager.LayoutParams lp) {\n        lp.flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;\n        super.modifyWindowLayoutParams(lp);\n    }\n\n    public interface OnBlankClickListener {\n        void onBlankClick(QMUIFullScreenPopup popup);\n    }\n\n    class RootView extends QMUIConstraintLayout {\n        private boolean mShouldInvokeBlackClickWhenTouchUp = false;\n\n        public RootView(Context context) {\n            super(context);\n        }\n\n        @Override\n        public boolean onTouchEvent(MotionEvent event) {\n            int action = event.getActionMasked();\n            if (mOnBlankClickListener == null) {\n                return true;\n            }\n            if (action == MotionEvent.ACTION_DOWN) {\n                mShouldInvokeBlackClickWhenTouchUp = isTouchInBlack(event);\n            } else if (action == MotionEvent.ACTION_MOVE) {\n                mShouldInvokeBlackClickWhenTouchUp = mShouldInvokeBlackClickWhenTouchUp && isTouchInBlack(event);\n            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n                mShouldInvokeBlackClickWhenTouchUp = mShouldInvokeBlackClickWhenTouchUp && isTouchInBlack(event);\n                if (mShouldInvokeBlackClickWhenTouchUp) {\n                    mOnBlankClickListener.onBlankClick(QMUIFullScreenPopup.this);\n                }\n            }\n            return true;\n        }\n\n        private boolean isTouchInBlack(MotionEvent event) {\n            View childView = findChildViewUnder(event.getX(), event.getY());\n            boolean isBlank = childView == null;\n            if (!isBlank && (childView instanceof IBlankTouchDetector)) {\n                MotionEvent e = MotionEvent.obtain(event);\n                int offsetX = getScrollX() - childView.getLeft();\n                int offsetY = getScrollY() - childView.getTop();\n                e.offsetLocation(offsetX, offsetY);\n                isBlank = ((IBlankTouchDetector) childView).isTouchInBlank(e);\n                e.recycle();\n            }\n            return isBlank;\n        }\n\n\n        private View findChildViewUnder(float x, float y) {\n            final int count = getChildCount();\n            for (int i = count - 1; i >= 0; i--) {\n                final View child = getChildAt(i);\n                final float translationX = child.getTranslationX();\n                final float translationY = child.getTranslationY();\n                if (x >= child.getLeft() + translationX\n                        && x <= child.getRight() + translationX\n                        && y >= child.getTop() + translationY\n                        && y <= child.getBottom() + translationY) {\n                    return child;\n                }\n            }\n            return null;\n        }\n\n        @Override\n        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n            super.onLayout(changed, left, top, right, bottom);\n            for (ViewInfo viewInfo : mViews) {\n                View view = viewInfo.view;\n                QMUIViewHelper.getOrCreateOffsetHelper(view).onViewLayout();\n            }\n        }\n    }\n\n    class ViewInfo {\n        private View view;\n        private ConstraintLayout.LayoutParams lp;\n\n        public ViewInfo(View view, ConstraintLayout.LayoutParams lp) {\n            this.view = view;\n            this.lp = lp;\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUINormalPopup.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.AnimRes;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\nimport com.qmuiteam.qmui.skin.IQMUISkinDispatchInterceptor;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n\npublic class QMUINormalPopup<T extends QMUIBasePopup> extends QMUIBasePopup<T> {\n    public static final int ANIM_AUTO = 0;\n    public static final int ANIM_GROW_FROM_LEFT = 1;\n    public static final int ANIM_GROW_FROM_RIGHT = 2;\n    public static final int ANIM_GROW_FROM_CENTER = 3;\n    public static final int ANIM_SPEC = 4;\n\n    @IntDef(value = {ANIM_AUTO, ANIM_GROW_FROM_LEFT, ANIM_GROW_FROM_RIGHT, ANIM_GROW_FROM_CENTER, ANIM_SPEC})\n    public @interface AnimStyle {\n    }\n\n    public static final int DIRECTION_TOP = 0;\n    public static final int DIRECTION_BOTTOM = 1;\n    public static final int DIRECTION_CENTER_IN_SCREEN = 2;\n\n    @IntDef({DIRECTION_CENTER_IN_SCREEN, DIRECTION_TOP, DIRECTION_BOTTOM})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Direction {\n    }\n\n    protected @AnimStyle int mAnimStyle;\n    protected int mSpecAnimStyle;\n    private int mEdgeProtectionTop;\n    private int mEdgeProtectionLeft;\n    private int mEdgeProtectionRight;\n    private int mEdgeProtectionBottom;\n    private boolean mShowArrow = true;\n    private boolean mAddShadow = false;\n    private int mRadius = NOT_SET;\n    private int mBorderColor = Color.TRANSPARENT;\n    private int mBorderUsedColor = Color.TRANSPARENT;\n    private int mBorderColorAttr = R.attr.qmui_skin_support_popup_border_color;\n    private boolean mIsBorderColorSet = false;\n    private int mBorderWidth = NOT_SET;\n    private int mShadowElevation = NOT_SET;\n    private float mShadowAlpha = 0f;\n    private int mShadowInset = NOT_SET;\n    private int mBgColor = Color.TRANSPARENT;\n    private boolean mIsBgColorSet= false;\n    private int mBgUsedColor = Color.TRANSPARENT;\n    private int mBgColorAttr = R.attr.qmui_skin_support_popup_bg;\n    private int mOffsetX = 0;\n    private int mOffsetYIfTop = 0;\n    private int mOffsetYIfBottom = 0;\n    private @Direction int mPreferredDirection = DIRECTION_BOTTOM;\n    protected final int mInitWidth;\n    protected final int mInitHeight;\n    private int mArrowWidth = NOT_SET;\n    private int mArrowHeight = NOT_SET;\n    private boolean mRemoveBorderWhenShadow = false;\n    private DecorRootView mDecorRootView;\n    private View mContentView;\n    private boolean mForceMeasureIfNeeded;\n\n    public QMUINormalPopup(Context context, int width, int height){\n        this(context, width, height, true);\n    }\n\n    public QMUINormalPopup(Context context, int width, int height, boolean forceMeasureIfNeeded) {\n        super(context);\n        mInitWidth = width;\n        mInitHeight = height;\n        mDecorRootView = new DecorRootView(context);\n        mWindow.setContentView(mDecorRootView);\n        mForceMeasureIfNeeded = forceMeasureIfNeeded;\n    }\n\n    public T arrow(boolean showArrow) {\n        mShowArrow = showArrow;\n        return (T) this;\n    }\n\n    public T arrowSize(int width, int height) {\n        mArrowWidth = width;\n        mArrowHeight = height;\n        return (T) this;\n    }\n\n    public T shadow(boolean addShadow) {\n        mAddShadow = addShadow;\n        return (T) this;\n    }\n\n    public T removeBorderWhenShadow(boolean removeBorderWhenShadow) {\n        mRemoveBorderWhenShadow = removeBorderWhenShadow;\n        return (T) this;\n    }\n\n    public T animStyle(@AnimStyle int animStyle) {\n        mAnimStyle = animStyle;\n        return (T) this;\n    }\n\n    public T customAnimStyle(@AnimRes int animStyle) {\n        mAnimStyle = ANIM_SPEC;\n        mSpecAnimStyle = animStyle;\n        return (T) this;\n    }\n\n    public T radius(int radius) {\n        mRadius = radius;\n        return (T) this;\n    }\n\n    public T shadowElevation(int shadowElevation, float shadowAlpha) {\n        mShadowAlpha = shadowAlpha;\n        mShadowElevation = shadowElevation;\n        return (T) this;\n    }\n\n    public T shadowInset(int shadowInset) {\n        mShadowInset = shadowInset;\n        return (T) this;\n    }\n\n    public T edgeProtection(int distance) {\n        mEdgeProtectionLeft = distance;\n        mEdgeProtectionRight = distance;\n        mEdgeProtectionTop = distance;\n        mEdgeProtectionBottom = distance;\n        return (T) this;\n    }\n\n    public T edgeProtection(int left, int top, int right, int bottom) {\n        mEdgeProtectionLeft = left;\n        mEdgeProtectionTop = top;\n        mEdgeProtectionRight = right;\n        mEdgeProtectionBottom = bottom;\n        return (T) this;\n    }\n\n    public T offsetX(int offsetX) {\n        mOffsetX = offsetX;\n        return (T) this;\n    }\n\n    public T offsetYIfTop(int y) {\n        mOffsetYIfTop = y;\n        return (T) this;\n    }\n\n    public T offsetYIfBottom(int y) {\n        mOffsetYIfBottom = y;\n        return (T) this;\n    }\n\n    public T preferredDirection(@Direction int preferredDirection) {\n        mPreferredDirection = preferredDirection;\n        return (T) this;\n    }\n\n    public T view(View contentView) {\n        mContentView = contentView;\n        return (T) this;\n    }\n\n    public T view(@LayoutRes int contentViewResId) {\n        return view(LayoutInflater.from(mContext).inflate(contentViewResId, null));\n    }\n\n    @NonNull\n    public View getDecorRootView(){\n        return mDecorRootView;\n    }\n\n    public View getWindowContentChildView(){\n        View self = mDecorRootView;\n        ViewParent parent = mDecorRootView.getParent();\n        while (parent instanceof View){\n            if(((View) parent).getId() == android.R.id.content){\n                return self;\n            }\n            self = (View)parent;\n            parent = self.getParent();\n        }\n        return self;\n    }\n\n    @Nullable\n    public View getContentView(){\n        return mContentView;\n    }\n\n    public T borderWidth(int borderWidth) {\n        mBorderWidth = borderWidth;\n        return (T) this;\n    }\n\n    public T borderColor(int borderColor) {\n        mBorderColor = borderColor;\n        mIsBorderColorSet = true;\n        return (T) this;\n    }\n\n    public int getBgColor() {\n        return mBgColor;\n    }\n\n    public int getBgColorAttr() {\n        return mBgColorAttr;\n    }\n\n    public int getBorderColor() {\n        return mBorderColor;\n    }\n\n    public int getBorderColorAttr() {\n        return mBorderColorAttr;\n    }\n\n    public T bgColor(int bgColor) {\n        mBgColor = bgColor;\n        mIsBgColorSet = true;\n        return (T) this;\n    }\n\n    public T borderColorAttr(int borderColorAttr) {\n        mBorderColorAttr = borderColorAttr;\n        if(borderColorAttr != 0){\n            mIsBorderColorSet = false;\n        }\n        return (T) this;\n    }\n\n    public T bgColorAttr(int bgColorAttr) {\n        mBgColorAttr = bgColorAttr;\n        if(bgColorAttr != 0){\n            mIsBgColorSet = false;\n        }\n        return (T) this;\n    }\n\n    class ShowInfo {\n        private int[] anchorRootLocation = new int[2];\n        private Rect anchorFrame = new Rect();\n        Rect visibleWindowFrame = new Rect();\n        int width;\n        int height;\n        int x;\n        int y;\n        int anchorHeight;\n        int anchorCenter;\n        int direction = mPreferredDirection;\n        int contentWidthMeasureSpec;\n        int contentHeightMeasureSpec;\n        int decorationLeft = 0;\n        int decorationRight = 0;\n        int decorationTop = 0;\n        int decorationBottom = 0;\n\n        ShowInfo(View anchor, int anchorAreaLeft, int anchorAreaTop, int anchorAreaRight, int anchorAreaBottom) {\n            this.anchorHeight = anchorAreaBottom - anchorAreaTop;\n            // for muti window\n            anchor.getRootView().getLocationOnScreen(anchorRootLocation);\n            int[] anchorLocation = new int[2];\n            anchor.getLocationOnScreen(anchorLocation);\n            this.anchorCenter = anchorLocation[0] + (anchorAreaLeft + anchorAreaRight) / 2;\n            anchor.getWindowVisibleDisplayFrame(visibleWindowFrame);\n            anchorFrame.left = anchorLocation [0] + anchorAreaLeft;\n            anchorFrame.top = anchorLocation[1] + anchorAreaTop;\n            anchorFrame.right = anchorLocation [0] + anchorAreaRight;\n            anchorFrame.bottom = anchorLocation [1] + anchorAreaBottom;\n        }\n\n        ShowInfo(View anchor){\n            this(anchor, 0, 0, anchor.getWidth(), anchor.getHeight());\n        }\n\n\n        float anchorProportion() {\n            return (anchorCenter - x) / (float) width;\n        }\n\n        int windowWidth() {\n            return decorationLeft + width + decorationRight;\n        }\n\n        int windowHeight() {\n            return decorationTop + height + decorationBottom;\n        }\n\n        int getVisibleWidth() {\n            return visibleWindowFrame.width();\n        }\n\n        int getVisibleHeight() {\n            return visibleWindowFrame.height();\n        }\n\n        int getWindowX() {\n            return x - anchorRootLocation[0];\n        }\n\n        int getWindowY() {\n            return y - anchorRootLocation[1];\n        }\n    }\n\n    private boolean shouldShowShadow() {\n        return mAddShadow && QMUILayoutHelper.useFeature();\n    }\n\n    public T show(@NonNull View anchor) {\n        return show(anchor, 0, 0, anchor.getWidth(), anchor.getHeight());\n    }\n\n    public T show(@NonNull View anchor, int anchorAreaLeft, int anchorAreaTop, int anchorAreaRight, int anchorAreaBottom){\n        if (mContentView == null) {\n            throw new RuntimeException(\"you should call view() to set your content view\");\n        }\n        decorateContentView();\n        ShowInfo showInfo = new ShowInfo(anchor, anchorAreaLeft, anchorAreaTop, anchorAreaRight, anchorAreaBottom);\n        calculateWindowSize(showInfo);\n        calculateXY(showInfo);\n        adjustShowInfo(showInfo);\n        mDecorRootView.setShowInfo(showInfo);\n        setAnimationStyle(showInfo.anchorProportion(), showInfo.direction);\n        mWindow.setWidth(showInfo.windowWidth());\n        mWindow.setHeight(showInfo.windowHeight());\n        showAtLocation(anchor, showInfo.getWindowX(), showInfo.getWindowY());\n        return (T) this;\n    }\n\n    private void decorateContentView() {\n        ContentView contentView = ContentView.wrap(mContentView, mInitWidth, mInitHeight);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        if (mIsBorderColorSet) {\n            mBorderUsedColor = mBorderColor;\n        } else if (mBorderColorAttr != 0) {\n            mBorderUsedColor = QMUIResHelper.getAttrColor(mContext, mBorderColorAttr);\n            builder.border(mBorderColorAttr);\n        }\n        if (mIsBgColorSet) {\n            mBgUsedColor = mBgColor;\n        } else if (mBgColorAttr != 0) {\n            mBgUsedColor = QMUIResHelper.getAttrColor(mContext, mBgColorAttr);\n            builder.background(mBgColorAttr);\n        }\n\n        if (mBorderWidth == NOT_SET) {\n            mBorderWidth = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_border_width);\n        }\n\n        QMUISkinHelper.setSkinValue(contentView, builder);\n        builder.release();\n        contentView.setBackgroundColor(mBgUsedColor);\n        contentView.setBorderColor(mBorderUsedColor);\n        contentView.setBorderWidth(mBorderWidth);\n        contentView.setShowBorderOnlyBeforeL(mRemoveBorderWhenShadow);\n        if (mRadius == NOT_SET) {\n            mRadius = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_radius);\n        }\n\n        if (shouldShowShadow()) {\n            contentView.setRadiusAndShadow(mRadius, mShadowElevation, mShadowAlpha);\n        } else {\n            contentView.setRadius(mRadius);\n        }\n        mDecorRootView.setContentView(contentView);\n    }\n\n    private void adjustShowInfo(ShowInfo showInfo) {\n        if (shouldShowShadow()) {\n            if (mShadowElevation == NOT_SET) {\n                mShadowElevation = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_shadow_elevation);\n                mShadowAlpha = QMUIResHelper.getAttrFloatValue(mContext, R.attr.qmui_popup_shadow_alpha);\n            }\n            if (mShadowInset == NOT_SET) {\n                mShadowInset = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_shadow_inset);\n            }\n\n            int originX = showInfo.x, originY = showInfo.y;\n            if (originX - mShadowInset > showInfo.visibleWindowFrame.left) {\n                showInfo.x -= mShadowInset;\n                showInfo.decorationLeft = mShadowInset;\n            } else {\n                showInfo.decorationLeft = originX - showInfo.visibleWindowFrame.left;\n                showInfo.x = showInfo.visibleWindowFrame.left;\n            }\n            if (originX + showInfo.width + mShadowInset < showInfo.visibleWindowFrame.right) {\n                showInfo.decorationRight = mShadowInset;\n            } else {\n                showInfo.decorationRight = showInfo.visibleWindowFrame.right - originX - showInfo.width;\n            }\n            if (originY - mShadowInset > showInfo.visibleWindowFrame.top) {\n                showInfo.y -= mShadowInset;\n                showInfo.decorationTop = mShadowInset;\n            } else {\n                showInfo.decorationTop = originY - showInfo.visibleWindowFrame.top;\n                showInfo.y = showInfo.visibleWindowFrame.top;\n            }\n            if (originY + showInfo.height + mShadowInset < showInfo.visibleWindowFrame.bottom) {\n                showInfo.decorationBottom = mShadowInset;\n            } else {\n                showInfo.decorationBottom = showInfo.visibleWindowFrame.bottom - originY - showInfo.height;\n            }\n        }\n\n        if (mShowArrow && showInfo.direction != DIRECTION_CENTER_IN_SCREEN) {\n            if (mArrowWidth == NOT_SET) {\n                mArrowWidth = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_arrow_width);\n            }\n            if (mArrowHeight == NOT_SET) {\n                mArrowHeight = QMUIResHelper.getAttrDimen(mContext, R.attr.qmui_popup_arrow_height);\n            }\n            if (showInfo.direction == DIRECTION_BOTTOM) {\n                if (shouldShowShadow()) {\n                    showInfo.y += Math.min(mShadowInset, mArrowHeight);\n                }\n                showInfo.decorationTop = Math.max(showInfo.decorationTop, mArrowHeight);\n            } else if (showInfo.direction == DIRECTION_TOP) {\n                showInfo.decorationBottom = Math.max(showInfo.decorationBottom, mArrowHeight);\n                showInfo.y -= mArrowHeight;\n            }\n        }\n    }\n\n    private void calculateXY(ShowInfo showInfo) {\n        if (showInfo.anchorCenter < showInfo.visibleWindowFrame.left + showInfo.getVisibleWidth() / 2) { // anchor point on the left\n            showInfo.x = Math.max(mEdgeProtectionLeft + showInfo.visibleWindowFrame.left, showInfo.anchorCenter - showInfo.width / 2 + mOffsetX);\n        } else { // anchor point on the left\n            showInfo.x = Math.min(\n                    showInfo.visibleWindowFrame.right - mEdgeProtectionRight - showInfo.width,\n                    showInfo.anchorCenter - showInfo.width / 2 + mOffsetX);\n        }\n        int nextDirection = DIRECTION_CENTER_IN_SCREEN;\n        if (mPreferredDirection == DIRECTION_BOTTOM) {\n            nextDirection = DIRECTION_TOP;\n        } else if (mPreferredDirection == DIRECTION_TOP) {\n            nextDirection = DIRECTION_BOTTOM;\n        }\n        handleDirection(showInfo, mPreferredDirection, nextDirection);\n    }\n\n    private void handleDirection(ShowInfo showInfo, int currentDirection, int nextDirection) {\n        if (currentDirection == DIRECTION_CENTER_IN_SCREEN) {\n            showInfo.x = showInfo.visibleWindowFrame.left + (showInfo.getVisibleWidth() - showInfo.width) / 2;\n            showInfo.y = showInfo.visibleWindowFrame.top + (showInfo.getVisibleHeight() - showInfo.height) / 2;\n            showInfo.direction = DIRECTION_CENTER_IN_SCREEN;\n        } else if (currentDirection == DIRECTION_TOP) {\n            showInfo.y = showInfo.anchorFrame.top - showInfo.height - mOffsetYIfTop;\n            if (showInfo.y < mEdgeProtectionTop + showInfo.visibleWindowFrame.top) {\n                handleDirection(showInfo, nextDirection, DIRECTION_CENTER_IN_SCREEN);\n            } else {\n                showInfo.direction = DIRECTION_TOP;\n            }\n        } else if (currentDirection == DIRECTION_BOTTOM) {\n            showInfo.y = showInfo.anchorFrame.top + showInfo.anchorHeight + mOffsetYIfBottom;\n            if (showInfo.y > showInfo.visibleWindowFrame.bottom - mEdgeProtectionBottom - showInfo.height) {\n                handleDirection(showInfo, nextDirection, DIRECTION_CENTER_IN_SCREEN);\n            } else {\n                showInfo.direction = DIRECTION_BOTTOM;\n            }\n        }\n    }\n\n    protected int proxyWidth(int width) {\n        return width;\n    }\n\n    protected int proxyHeight(int height) {\n        return height;\n    }\n\n    private void calculateWindowSize(ShowInfo showInfo) {\n        boolean needMeasureForWidth = false, needMeasureForHeight = false;\n        if (mInitWidth > 0) {\n            showInfo.width = proxyWidth(mInitWidth);\n            showInfo.contentWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                    showInfo.width, View.MeasureSpec.EXACTLY);\n        } else {\n            int maxWidth = showInfo.getVisibleWidth() - mEdgeProtectionLeft - mEdgeProtectionRight;\n            if (mInitWidth == ViewGroup.LayoutParams.MATCH_PARENT) {\n                showInfo.width = proxyWidth(maxWidth);\n                showInfo.contentWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                        showInfo.width, View.MeasureSpec.EXACTLY);\n            } else {\n                needMeasureForWidth = true;\n                showInfo.contentWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                        proxyWidth(maxWidth), View.MeasureSpec.AT_MOST);\n            }\n        }\n        if (mInitHeight > 0) {\n            showInfo.height = proxyHeight(mInitHeight);\n            showInfo.contentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                    showInfo.height, View.MeasureSpec.EXACTLY);\n        } else {\n            int maxHeight = showInfo.getVisibleHeight() - mEdgeProtectionTop - mEdgeProtectionBottom;\n            if (mInitHeight == ViewGroup.LayoutParams.MATCH_PARENT) {\n                showInfo.height = proxyHeight(maxHeight);\n                showInfo.contentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                        showInfo.height, View.MeasureSpec.EXACTLY);\n            } else {\n                needMeasureForHeight = true;\n                showInfo.contentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(\n                        proxyHeight(maxHeight), View.MeasureSpec.AT_MOST);\n            }\n        }\n\n        if (mForceMeasureIfNeeded && (needMeasureForWidth || needMeasureForHeight)) {\n            mContentView.measure(\n                    showInfo.contentWidthMeasureSpec, showInfo.contentHeightMeasureSpec);\n            if (needMeasureForWidth) {\n                showInfo.width = proxyWidth(mContentView.getMeasuredWidth());\n            }\n            if (needMeasureForHeight) {\n                showInfo.height = proxyHeight(mContentView.getMeasuredHeight());\n            }\n        }\n    }\n\n    private void setAnimationStyle(float anchorProportion, @Direction int direction) {\n        boolean onTop = direction == DIRECTION_TOP;\n        switch (mAnimStyle) {\n            case ANIM_GROW_FROM_LEFT:\n                mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Left : R.style.QMUI_Animation_PopDownMenu_Left);\n                break;\n\n            case ANIM_GROW_FROM_RIGHT:\n                mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Right : R.style.QMUI_Animation_PopDownMenu_Right);\n                break;\n\n            case ANIM_GROW_FROM_CENTER:\n                mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Center : R.style.QMUI_Animation_PopDownMenu_Center);\n                break;\n            case ANIM_AUTO:\n                if (anchorProportion <= 0.25f) {\n                    mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Left : R.style.QMUI_Animation_PopDownMenu_Left);\n                } else if (anchorProportion > 0.25f && anchorProportion < 0.75f) {\n                    mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Center : R.style.QMUI_Animation_PopDownMenu_Center);\n                } else {\n                    mWindow.setAnimationStyle(onTop ? R.style.QMUI_Animation_PopUpMenu_Right : R.style.QMUI_Animation_PopDownMenu_Right);\n                }\n                break;\n            case ANIM_SPEC:\n                mWindow.setAnimationStyle(mSpecAnimStyle);\n                break;\n        }\n    }\n\n    static class ContentView extends QMUIFrameLayout {\n        private ContentView(Context context) {\n            super(context);\n        }\n\n        static ContentView wrap(View businessView, int width, int height) {\n            ContentView contentView = new ContentView(businessView.getContext());\n            if (businessView.getParent() != null) {\n                ((ViewGroup) businessView.getParent()).removeView(businessView);\n            }\n            contentView.addView(businessView, new LayoutParams(width, height));\n            return contentView;\n        }\n    }\n\n    class DecorRootView extends FrameLayout implements IQMUISkinDispatchInterceptor {\n        private ShowInfo mShowInfo;\n        private View mContentView;\n        private Paint mArrowPaint;\n        private Path mArrowPath;\n        private RectF mArrowSaveRect = new RectF();\n        private PorterDuffXfermode mArrowAlignMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);\n\n        private int mPendingWidth;\n        private int mPendingHeight;\n        private Runnable mUpdateWindowAction = new Runnable() {\n            @Override\n            public void run() {\n                mShowInfo.width = mPendingWidth;\n                mShowInfo.height = mPendingHeight;\n                calculateXY(mShowInfo);\n                adjustShowInfo(mShowInfo);\n                mWindow.update(mShowInfo.getWindowX(), mShowInfo.getWindowY(), mShowInfo.windowWidth(), mShowInfo.windowHeight());\n            }\n        };\n\n        private DecorRootView(Context context) {\n            super(context);\n            mArrowPaint = new Paint();\n            mArrowPaint.setAntiAlias(true);\n            mArrowPath = new Path();\n        }\n\n        public void setShowInfo(ShowInfo showInfo) {\n            mShowInfo = showInfo;\n            requestFocus();\n        }\n\n        public void setContentView(View contentView) {\n            if (mContentView != null) {\n                removeView(mContentView);\n            }\n            if (contentView.getParent() != null) {\n                ((ViewGroup) contentView.getParent()).removeView(contentView);\n            }\n            mContentView = contentView;\n            addView(contentView);\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            removeCallbacks(mUpdateWindowAction);\n            if(mShowInfo == null){\n                setMeasuredDimension(0, 0);\n                return;\n            }\n            if (mContentView != null) {\n                mContentView.measure(mShowInfo.contentWidthMeasureSpec, mShowInfo.contentHeightMeasureSpec);\n                int measuredWidth = mContentView.getMeasuredWidth();\n                int measuredHeight = mContentView.getMeasuredHeight();\n                if (mShowInfo.width != measuredWidth || mShowInfo.height != measuredHeight) {\n                    mPendingWidth = measuredWidth;\n                    mPendingHeight = measuredHeight;\n                    post(mUpdateWindowAction);\n                }\n            }\n            setMeasuredDimension(mShowInfo.windowWidth(), mShowInfo.windowHeight());\n        }\n\n        @Override\n        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n            if (mContentView != null && mShowInfo != null) {\n                mContentView.layout(mShowInfo.decorationLeft, mShowInfo.decorationTop,\n                        mShowInfo.width + mShowInfo.decorationLeft,\n                        mShowInfo.height + mShowInfo.decorationTop);\n            }\n        }\n\n        @Override\n        protected void onAttachedToWindow() {\n            super.onAttachedToWindow();\n            removeCallbacks(mUpdateWindowAction);\n        }\n\n        @Override\n        public boolean intercept(int skinIndex, @NotNull Resources.Theme theme) {\n            if (!mIsBorderColorSet && mBorderColorAttr != 0) {\n                mBorderUsedColor = QMUIResHelper.getAttrColor(theme, mBorderColorAttr);\n            }\n            if (!mIsBgColorSet && mBgColorAttr != 0) {\n                mBgUsedColor = QMUIResHelper.getAttrColor(theme, mBgColorAttr);\n            }\n            return false;\n        }\n\n        @Override\n        protected void dispatchDraw(Canvas canvas) {\n            super.dispatchDraw(canvas);\n            if(mShowInfo == null){\n                return;\n            }\n            if (mShowArrow) {\n                if (mShowInfo.direction == DIRECTION_TOP) {\n                    canvas.save();\n                    mArrowSaveRect.set(0f, 0f, mShowInfo.width, mShowInfo.height);\n                    mArrowPaint.setStyle(Paint.Style.FILL);\n                    mArrowPaint.setColor(mBgUsedColor);\n                    mArrowPaint.setXfermode(null);\n                    int l = mShowInfo.anchorCenter - mShowInfo.x - mArrowWidth / 2;\n                    l = Math.min(Math.max(l, mShowInfo.decorationLeft),\n                            getWidth() - mShowInfo.decorationRight - mArrowWidth);\n                    int t = mShowInfo.decorationTop + mShowInfo.height - mBorderWidth;\n                    canvas.translate(l, t);\n                    mArrowPath.reset();\n                    mArrowPath.setLastPoint(-mArrowWidth / 2f, -mArrowHeight);\n                    mArrowPath.lineTo(mArrowWidth / 2f, mArrowHeight);\n                    mArrowPath.lineTo(mArrowWidth * 3 /2f, -mArrowHeight);\n                    mArrowPath.close();\n                    canvas.drawPath(mArrowPath, mArrowPaint);\n                    if (!mRemoveBorderWhenShadow || !shouldShowShadow()) {\n                        mArrowSaveRect.set(0f, -mBorderWidth, mArrowWidth, mArrowHeight + mBorderWidth);\n                        int saveLayer = canvas.saveLayer(mArrowSaveRect, mArrowPaint, Canvas.ALL_SAVE_FLAG);\n                        mArrowPaint.setStrokeWidth(mBorderWidth);\n                        mArrowPaint.setColor(mBorderUsedColor);\n                        mArrowPaint.setStyle(Paint.Style.STROKE);\n                        canvas.drawPath(mArrowPath, mArrowPaint);\n                        mArrowPaint.setXfermode(mArrowAlignMode);\n                        mArrowPaint.setStyle(Paint.Style.FILL);\n                        canvas.drawRect(0f, -mBorderWidth, mArrowWidth, 0, mArrowPaint);\n                        canvas.restoreToCount(saveLayer);\n                    }\n                    canvas.restore();\n                } else if (mShowInfo.direction == DIRECTION_BOTTOM) {\n                    canvas.save();\n                    mArrowPaint.setStyle(Paint.Style.FILL);\n                    mArrowPaint.setXfermode(null);\n                    mArrowPaint.setColor(mBgUsedColor);\n                    int l = mShowInfo.anchorCenter - mShowInfo.x - mArrowWidth / 2;\n                    l = Math.min(Math.max(l, mShowInfo.decorationLeft),\n                            getWidth() - mShowInfo.decorationRight - mArrowWidth);\n                    int t = mShowInfo.decorationTop + mBorderWidth;\n                    canvas.translate(l, t);\n                    mArrowPath.reset();\n                    mArrowPath.setLastPoint(-mArrowWidth / 2f, mArrowHeight);\n                    mArrowPath.lineTo(mArrowWidth / 2f, -mArrowHeight);\n                    mArrowPath.lineTo(mArrowWidth * 3 / 2f, mArrowHeight);\n                    mArrowPath.close();\n                    canvas.drawPath(mArrowPath, mArrowPaint);\n                    if (!mRemoveBorderWhenShadow || !shouldShowShadow()) {\n                        mArrowSaveRect.set(0, -mArrowHeight - mBorderWidth, mArrowWidth, mBorderWidth);\n                        int saveLayer = canvas.saveLayer(mArrowSaveRect, mArrowPaint, Canvas.ALL_SAVE_FLAG);\n                        mArrowPaint.setStrokeWidth(mBorderWidth);\n                        mArrowPaint.setStyle(Paint.Style.STROKE);\n                        mArrowPaint.setColor(mBorderUsedColor);\n                        canvas.drawPath(mArrowPath, mArrowPaint);\n                        mArrowPaint.setXfermode(mArrowAlignMode);\n                        mArrowPaint.setStyle(Paint.Style.FILL);\n                        canvas.drawRect(0, 0, mArrowWidth, mBorderWidth, mArrowPaint);\n                        canvas.restoreToCount(saveLayer);\n                    }\n                    canvas.restore();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUIPopup.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\n\npublic class QMUIPopup extends QMUINormalPopup<QMUIPopup> {\n\n    public QMUIPopup(Context context, int width, int height) {\n        this(context, width, height, true);\n    }\n\n    public QMUIPopup(Context context, int width, int height, boolean forceMeasureIfNeeded) {\n        super(context, width, height, forceMeasureIfNeeded);\n    }\n\n    @Override\n    public QMUIPopup show(@NonNull View anchor) {\n        return super.show(anchor);\n    }\n\n    @Override\n    public QMUIPopup show(@NonNull View anchor, int anchorAreaLeft, int anchorAreaTop, int anchorAreaRight, int anchorAreaBottom) {\n        return super.show(anchor, anchorAreaLeft, anchorAreaTop, anchorAreaRight, anchorAreaBottom);\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUIPopups.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.BaseAdapter;\nimport android.widget.ListView;\n\nimport com.qmuiteam.qmui.widget.QMUIWrapContentListView;\n\npublic class QMUIPopups {\n\n    public static QMUIPopup popup(Context context) {\n        return new QMUIPopup(context,\n                ViewGroup.LayoutParams.WRAP_CONTENT,\n                ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    public static QMUIPopup popup(Context context, int width) {\n        return new QMUIPopup(context,\n                width,\n                ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    public static QMUIPopup popup(Context context, int width, int height) {\n        return new QMUIPopup(context, width, height);\n    }\n\n    /**\n     * show a list with popup\n     *\n     * @param context             activity context\n     * @param width               the with for the popup content\n     * @param maxHeight           the max height of popup, it is scrollable if the content is higher then maxHeight\n     * @param adapter             the adapter for the list view\n     * @param onItemClickListener the onItemClickListener for list item view\n     * @return QMUIPopup\n     */\n    public static QMUIPopup listPopup(Context context, int width, int maxHeight,\n                                      BaseAdapter adapter,\n                                      AdapterView.OnItemClickListener onItemClickListener) {\n        ListView listView = new QMUIWrapContentListView(context, maxHeight);\n        listView.setAdapter(adapter);\n        listView.setVerticalScrollBarEnabled(false);\n        listView.setOnItemClickListener(onItemClickListener);\n        listView.setDivider(null);\n        return popup(context, width).view(listView);\n    }\n\n    public static QMUIFullScreenPopup fullScreenPopup(Context context) {\n        return new QMUIFullScreenPopup(context);\n    }\n\n    public static QMUIQuickAction quickAction(Context context, int actionWidth, int actionHeight) {\n        return new QMUIQuickAction(context, ViewGroup.LayoutParams.WRAP_CONTENT, actionHeight)\n                .actionWidth(actionWidth)\n                .actionHeight(actionHeight);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/popup/QMUIQuickAction.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.popup;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.LinearSmoothScroller;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.QMUIConstraintLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView2;\n\nimport java.util.ArrayList;\nimport java.util.Objects;\n\npublic class QMUIQuickAction extends QMUINormalPopup<QMUIQuickAction> {\n\n    private ArrayList<Action> mActions = new ArrayList<>();\n    private int mActionWidth = ViewGroup.LayoutParams.WRAP_CONTENT;\n    private int mActionHeight;\n    private boolean mShowMoreArrowIfNeeded = true;\n    private int mMoreArrowWidth;\n    private int mPaddingHor;\n\n\n    public QMUIQuickAction(Context context, int width, int height){\n        this(context, width, height, true);\n    }\n\n    public QMUIQuickAction(Context context, int width, int height, boolean forceMeasureIfNeeded) {\n        super(context, width, height, forceMeasureIfNeeded);\n        mActionHeight = height;\n        mMoreArrowWidth = QMUIResHelper.getAttrDimen(context, R.attr.qmui_quick_action_more_arrow_width);\n        mPaddingHor = QMUIResHelper.getAttrDimen(context, R.attr.qmui_quick_action_padding_hor);\n    }\n\n    public QMUIQuickAction moreArrowWidth(int moreArrowWidth) {\n        mMoreArrowWidth = moreArrowWidth;\n        return this;\n    }\n\n    public QMUIQuickAction paddingHor(int paddingHor) {\n        mPaddingHor = paddingHor;\n        return this;\n    }\n\n    public QMUIQuickAction actionWidth(int actionWidth) {\n        mActionWidth = actionWidth;\n        return this;\n    }\n\n    public QMUIQuickAction actionHeight(int actionHeight) {\n        mActionHeight = actionHeight;\n        return this;\n    }\n\n    public QMUIQuickAction addAction(Action action) {\n        mActions.add(action);\n        return this;\n    }\n\n    public QMUIQuickAction showMoreArrowIfNeeded(boolean showMoreArrowIfNeeded) {\n        mShowMoreArrowIfNeeded = showMoreArrowIfNeeded;\n        return this;\n    }\n\n    @Override\n    protected int proxyWidth(int width) {\n        if (width > 0 && mActionWidth > 0) {\n            if (width >= mActionWidth * mActions.size() + 2 * mPaddingHor) {\n                return super.proxyWidth(width);\n            }\n            width = width - mPaddingHor - mMoreArrowWidth;\n            return mActionWidth * (width / mActionWidth) + mPaddingHor + mMoreArrowWidth;\n        }\n        return super.proxyWidth(width);\n    }\n\n\n    @Override\n    public QMUIQuickAction show(@NonNull View anchor) {\n        return super.show(anchor);\n    }\n\n    @Override\n    public QMUIQuickAction show(@NonNull View anchor, int anchorAreaLeft, int anchorAreaTop, int anchorAreaRight, int anchorAreaBottom) {\n        view(createContentView());\n        return super.show(anchor, anchorAreaLeft, anchorAreaTop, anchorAreaRight, anchorAreaBottom);\n    }\n\n\n    private ConstraintLayout createContentView() {\n        ConstraintLayout wrapper = new ConstraintLayout(mContext);\n        final RecyclerView recyclerView = new RecyclerView(mContext);\n        final LayoutManager layoutManager = new LayoutManager(mContext);\n        recyclerView.setLayoutManager(layoutManager);\n        recyclerView.setId(View.generateViewId());\n        recyclerView.setPadding(mPaddingHor, 0, mPaddingHor, 0);\n        recyclerView.setClipToPadding(false);\n        final Adapter adapter = new Adapter();\n        adapter.submitList(mActions);\n        recyclerView.setAdapter(adapter);\n        wrapper.addView(recyclerView);\n        if (mShowMoreArrowIfNeeded) {\n            AppCompatImageView leftMoreArrow = createMoreArrowView(true);\n            AppCompatImageView rightMoreArrow = createMoreArrowView(false);\n\n            leftMoreArrow.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    recyclerView.smoothScrollToPosition(0);\n                }\n            });\n            rightMoreArrow.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1);\n                }\n            });\n\n            ConstraintLayout.LayoutParams leftLp = new ConstraintLayout.LayoutParams(mMoreArrowWidth, 0);\n            leftLp.leftToLeft = recyclerView.getId();\n            leftLp.topToTop = recyclerView.getId();\n            leftLp.bottomToBottom = recyclerView.getId();\n            wrapper.addView(leftMoreArrow, leftLp);\n\n\n            ConstraintLayout.LayoutParams rightLp = new ConstraintLayout.LayoutParams(mMoreArrowWidth, 0);\n            rightLp.rightToRight = recyclerView.getId();\n            rightLp.topToTop = recyclerView.getId();\n            rightLp.bottomToBottom = recyclerView.getId();\n            wrapper.addView(rightMoreArrow, rightLp);\n\n            recyclerView.addItemDecoration(new ItemDecoration(leftMoreArrow, rightMoreArrow));\n        }\n        return wrapper;\n    }\n\n\n    protected AppCompatImageView createMoreArrowView(boolean isLeft) {\n        QMUIRadiusImageView2 arrowView = new QMUIRadiusImageView2(mContext);\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        if (isLeft) {\n            arrowView.setPadding(mPaddingHor, 0, 0, 0);\n            builder.src(R.attr.qmui_skin_support_quick_action_more_left_arrow);\n        } else {\n            arrowView.setPadding(0, 0, mPaddingHor, 0);\n            builder.src(R.attr.qmui_skin_support_quick_action_more_right_arrow);\n        }\n        builder.tintColor(R.attr.qmui_skin_support_quick_action_more_tint_color);\n        int bgColor = getBgColor();\n        int bgColorAttr = getBgColorAttr();\n        if (bgColorAttr != 0) {\n            builder.background(bgColorAttr);\n        }else if (bgColor != Color.TRANSPARENT) {\n            arrowView.setBackgroundColor(bgColor);\n        }\n        QMUISkinHelper.setSkinValue(arrowView, builder);\n        arrowView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n        arrowView.setVisibility(View.GONE);\n        arrowView.setAlpha(0f);\n        builder.release();\n        return arrowView;\n    }\n\n    private class ItemDecoration extends RecyclerView.ItemDecoration {\n        private AppCompatImageView leftMoreArrowView;\n        private AppCompatImageView rightMoreArrowView;\n        private boolean isLeftMoreShown = false;\n        private boolean isRightMoreShown = false;\n        private boolean isFirstDraw = true;\n        private int TOGGLE_DURATION = 60;\n\n        public ItemDecoration(AppCompatImageView leftMoreArrowView,\n                              AppCompatImageView rightMoreArrowView) {\n            this.leftMoreArrowView = leftMoreArrowView;\n            this.rightMoreArrowView = rightMoreArrowView;\n        }\n\n        private Runnable leftHideEndAction = new Runnable() {\n            @Override\n            public void run() {\n                leftMoreArrowView.setVisibility(View.GONE);\n            }\n        };\n\n        private Runnable rightHideEndAction = new Runnable() {\n            @Override\n            public void run() {\n                rightMoreArrowView.setVisibility(View.GONE);\n            }\n        };\n\n\n        @Override\n        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n            if (parent.canScrollHorizontally(-1)) {\n                if (!isLeftMoreShown) {\n                    isLeftMoreShown = true;\n                    leftMoreArrowView.setVisibility(View.VISIBLE);\n                    if (isFirstDraw) {\n                        leftMoreArrowView.setAlpha(1F);\n                    } else {\n                        leftMoreArrowView.animate()\n                                .alpha(1f)\n                                .setDuration(TOGGLE_DURATION)\n                                .start();\n                    }\n\n                }\n            } else {\n                if (isLeftMoreShown) {\n                    isLeftMoreShown = false;\n                    leftMoreArrowView.animate()\n                            .alpha(0f)\n                            .setDuration(TOGGLE_DURATION)\n                            .withEndAction(leftHideEndAction)\n                            .start();\n                }\n            }\n            if (parent.canScrollHorizontally(1)) {\n                if (!isRightMoreShown) {\n                    isRightMoreShown = true;\n                    rightMoreArrowView.setVisibility(View.VISIBLE);\n                    if (isFirstDraw) {\n                        rightMoreArrowView.setAlpha(1F);\n                    } else {\n                        rightMoreArrowView.animate()\n                                .setDuration(TOGGLE_DURATION)\n                                .alpha(1f)\n                                .start();\n                    }\n                }\n            } else {\n                if (isRightMoreShown) {\n                    isRightMoreShown = false;\n                    rightMoreArrowView.animate()\n                            .alpha(0f)\n                            .setDuration(TOGGLE_DURATION)\n                            .withEndAction(rightHideEndAction)\n                            .start();\n                }\n            }\n            isFirstDraw = false;\n        }\n    }\n\n    private class LayoutManager extends LinearLayoutManager {\n\n        private static final float MILLISECONDS_PER_INCH = 0.01f;\n\n        public LayoutManager(Context context) {\n            super(context, LinearLayoutManager.HORIZONTAL, false);\n        }\n\n        @Override\n        public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n            return new RecyclerView.LayoutParams(mActionWidth, mActionHeight);\n        }\n\n        @Override\n        public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {\n            final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {\n\n                @Override\n                protected int calculateTimeForScrolling(int dx) {\n                    return 100;\n                }\n            };\n\n            linearSmoothScroller.setTargetPosition(position);\n            startSmoothScroll(linearSmoothScroller);\n        }\n    }\n\n    private static class VH extends RecyclerView.ViewHolder implements View.OnClickListener {\n\n        private Callback callback;\n\n        public VH(@NonNull ItemView itemView, @NonNull Callback callback) {\n            super(itemView);\n            itemView.setOnClickListener(this);\n            this.callback = callback;\n        }\n\n        @Override\n        public void onClick(View v) {\n            callback.onClick(v, getAdapterPosition());\n        }\n\n        interface Callback {\n            void onClick(View v, int adapterPosition);\n        }\n    }\n\n    private class DiffCallback extends DiffUtil.ItemCallback<Action> {\n\n\n        @Override\n        public boolean areItemsTheSame(@NonNull Action action, @NonNull Action t1) {\n            return Objects.equals(action.text, t1.text) &&\n                    action.icon == t1.icon &&\n                    action.iconAttr == t1.iconAttr &&\n                    action.onClickListener == t1.onClickListener;\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull Action action, @NonNull Action t1) {\n            return action.textColorAttr == t1.textColorAttr &&\n                    action.iconTintColorAttr == t1.iconTintColorAttr;\n        }\n    }\n\n    protected ItemView createItemView() {\n        return new DefaultItemView(mContext);\n    }\n\n\n    private class Adapter extends ListAdapter<Action, VH> implements VH.Callback {\n\n        protected Adapter() {\n            super(new DiffCallback());\n        }\n\n        @NonNull\n        @Override\n        public VH onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {\n            return new VH(createItemView(), this);\n        }\n\n        @Override\n        public void onClick(View v, int adapterPosition) {\n            Action action = getItem(adapterPosition);\n            OnClickListener onClickListener = action.onClickListener;\n            if (onClickListener != null) {\n                onClickListener.onClick(QMUIQuickAction.this, action, adapterPosition);\n            }\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull VH vh, int i) {\n            ItemView view = (ItemView) vh.itemView;\n            view.render(getItem(i));\n        }\n    }\n\n    public static class Action {\n        @Nullable Drawable icon;\n        int iconRes;\n        @Nullable OnClickListener onClickListener;\n        @Nullable CharSequence text;\n        int iconAttr = 0;\n        int textColorAttr = R.attr.qmui_skin_support_quick_action_item_tint_color;\n        int iconTintColorAttr = R.attr.qmui_skin_support_quick_action_item_tint_color;\n\n        public Action iconAttr(int iconAttr) {\n            this.iconAttr = iconAttr;\n            return this;\n        }\n\n        public Action icon(Drawable icon) {\n            this.icon = icon;\n            return this;\n        }\n\n        public Action icon(int iconRes) {\n            this.iconRes = iconRes;\n            return this;\n        }\n\n        public Action onClick(OnClickListener onClickListener) {\n            this.onClickListener = onClickListener;\n            return this;\n        }\n\n        public Action text(CharSequence text) {\n            this.text = text;\n            return this;\n        }\n\n        public Action textColorAttr(int textColorAttr) {\n            this.textColorAttr = textColorAttr;\n            return this;\n        }\n\n        public Action iconTintColorAttr(int iconTintColorAttr) {\n            this.iconTintColorAttr = iconTintColorAttr;\n            return this;\n        }\n    }\n\n    public interface OnClickListener {\n        void onClick(QMUIQuickAction quickAction, Action action, int position);\n    }\n\n    public abstract static class ItemView extends QMUIConstraintLayout {\n\n        public ItemView(Context context) {\n            super(context);\n        }\n\n        public ItemView(Context context, AttributeSet attrs) {\n            super(context, attrs);\n        }\n\n        public abstract void render(Action action);\n    }\n\n    public static class DefaultItemView extends ItemView {\n        private AppCompatImageView mIconView;\n        private TextView mTextView;\n\n        public DefaultItemView(Context context) {\n            this(context, null);\n        }\n\n        public DefaultItemView(Context context, AttributeSet attrs) {\n            super(context, attrs);\n            int paddingHor = QMUIResHelper.getAttrDimen(\n                    context, R.attr.qmui_quick_action_item_padding_hor);\n            int paddingVer = QMUIResHelper.getAttrDimen(\n                    context, R.attr.qmui_quick_action_item_padding_ver);\n            setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n            mIconView = new AppCompatImageView(context);\n            mIconView.setId(QMUIViewHelper.generateViewId());\n            mTextView = new TextView(context);\n            mTextView.setId(QMUIViewHelper.generateViewId());\n            mTextView.setTextSize(10);\n            mTextView.setTypeface(Typeface.DEFAULT_BOLD);\n            setChangeAlphaWhenPress(true);\n            setChangeAlphaWhenDisable(true);\n\n            int wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT;\n            LayoutParams iconLp = new LayoutParams(wrapContent, wrapContent);\n            iconLp.leftToLeft = LayoutParams.PARENT_ID;\n            iconLp.rightToRight = LayoutParams.PARENT_ID;\n            iconLp.topToTop = LayoutParams.PARENT_ID;\n            iconLp.bottomToTop = mTextView.getId();\n            iconLp.verticalChainStyle = LayoutParams.CHAIN_PACKED;\n            addView(mIconView, iconLp);\n\n            LayoutParams textLp = new LayoutParams(wrapContent, wrapContent);\n            textLp.leftToLeft = LayoutParams.PARENT_ID;\n            textLp.rightToRight = LayoutParams.PARENT_ID;\n            textLp.topToBottom = mIconView.getId();\n            textLp.bottomToBottom = LayoutParams.PARENT_ID;\n            textLp.topMargin = QMUIResHelper.getAttrDimen(\n                    context, R.attr.qmui_quick_action_item_middle_space);\n            textLp.verticalChainStyle = LayoutParams.CHAIN_PACKED;\n            textLp.goneTopMargin = 0;\n            addView(mTextView, textLp);\n        }\n\n        @Override\n        public void render(Action action) {\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            if (action.icon != null || action.iconRes != 0) {\n                if (action.icon != null) {\n                    mIconView.setImageDrawable(action.icon.mutate());\n                } else {\n                    mIconView.setImageResource(action.iconRes);\n                }\n\n                if (action.iconTintColorAttr != 0) {\n                    builder.tintColor(action.iconTintColorAttr);\n                }\n                mIconView.setVisibility(View.VISIBLE);\n                QMUISkinHelper.setSkinValue(mIconView, builder);\n            } else if (action.iconAttr != 0) {\n                builder.src(action.iconAttr);\n                mIconView.setVisibility(View.VISIBLE);\n                QMUISkinHelper.setSkinValue(mIconView, builder);\n            } else {\n                mIconView.setVisibility(View.GONE);\n            }\n\n            mTextView.setText(action.text);\n            builder.clear();\n            builder.textColor(action.textColorAttr);\n            QMUISkinHelper.setSkinValue(mTextView, builder);\n            builder.release();\n        }\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUIAlwaysFollowOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\npublic class QMUIAlwaysFollowOffsetCalculator implements QMUIPullLayout.ActionViewOffsetCalculator {\n\n    @Override\n    public int calculateOffset(QMUIPullLayout.PullAction pullAction, int targetOffset) {\n        return targetOffset + pullAction.getActionInitOffset();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUICenterOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\npublic class QMUICenterOffsetCalculator implements QMUIPullLayout.ActionViewOffsetCalculator {\n    @Override\n    public int calculateOffset(QMUIPullLayout.PullAction pullAction, int targetOffset) {\n        if(targetOffset < pullAction.getTargetTriggerOffset()){\n            return targetOffset + pullAction.getActionInitOffset();\n        }\n        return (targetOffset - pullAction.getActionPullSize()) / 2;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUIFixToTargetOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\npublic class QMUIFixToTargetOffsetCalculator implements QMUIPullLayout.ActionViewOffsetCalculator {\n    @Override\n    public int calculateOffset(QMUIPullLayout.PullAction pullAction, int targetOffset) {\n        if (targetOffset < pullAction.getTargetTriggerOffset()) {\n            return targetOffset + pullAction.getActionInitOffset();\n        }\n        return pullAction.getTargetTriggerOffset() + pullAction.getActionInitOffset();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUIPullLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.OverScroller;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.NestedScrollingParent3;\nimport androidx.core.view.NestedScrollingParentHelper;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.Beta;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport static com.qmuiteam.qmui.QMUIInterpolatorStaticHolder.QUNITIC_INTERPOLATOR;\n\n@Beta\npublic class QMUIPullLayout extends FrameLayout implements NestedScrollingParent3 {\n    public static final float DEFAULT_PULL_RATE = 0.45f;\n    public static final float DEFAULT_FLING_FRACTION = 0.002f;\n    public static final float DEFAULT_SCROLL_SPEED_PER_PIXEL = 1.5f;\n    public static final int DEFAULT_MIN_SCROLL_DURATION = 300;\n    public static final int PULL_EDGE_LEFT = 0x01;\n    public static final int PULL_EDGE_TOP = 0x02;\n    public static final int PULL_EDGE_RIGHT = 0x04;\n    public static final int PULL_EDGE_BOTTOM = 0x08;\n    public static final int PUL_EDGE_ALL = PULL_EDGE_LEFT | PULL_EDGE_TOP | PULL_EDGE_RIGHT | PULL_EDGE_BOTTOM;\n\n    private static final int STATE_IDLE = 0;\n    private static final int STATE_PULLING = 1;\n    private static final int STATE_SETTLING_TO_TRIGGER_OFFSET = 2;\n    private static final int STATE_TRIGGERING= 3;\n    private static final int STATE_SETTLING_TO_INIT_OFFSET = 4;\n    private static final int STATE_SETTLING_DELIVER = 5;\n    private static final int STATE_SETTLING_FLING = 6;\n\n    @IntDef({PULL_EDGE_LEFT, PULL_EDGE_TOP, PULL_EDGE_RIGHT, PULL_EDGE_BOTTOM})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface PullEdge {\n    }\n\n\n    private int mEnabledEdges;\n    private View mTargetView;\n    private QMUIViewOffsetHelper mTargetOffsetHelper;\n    private PullAction mLeftPullAction = null;\n    private PullAction mTopPullAction = null;\n    private PullAction mRightPullAction = null;\n    private PullAction mBottomPullAction = null;\n    private ActionListener mActionListener;\n\n    // Array to be used for calls from v2 version of onNestedScroll to v3 version of onNestedScroll.\n    // This only exist to prevent GC and object instantiation costs that are present before API 21.\n    private final int[] mNestedScrollingV2ConsumedCompat = new int[2];\n    private StopTargetViewFlingImpl mStopTargetViewFlingImpl = DefaultStopTargetViewFlingImpl.getInstance();\n    private Runnable mStopTargetFlingRunnable = null;\n    private OverScroller mScroller;\n    private float mNestedPreFlingVelocityScaleDown = 10;\n    private int mMinScrollDuration = DEFAULT_MIN_SCROLL_DURATION;\n    private int mState = STATE_IDLE;\n\n    private final NestedScrollingParentHelper mNestedScrollingParentHelper;\n\n    public QMUIPullLayout(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QMUIPullLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIPullLayoutStyle);\n    }\n\n    public QMUIPullLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUIPullLayout, defStyleAttr, 0);\n        mEnabledEdges = array.getInt(R.styleable.QMUIPullLayout_qmui_pull_enable_edge, PUL_EDGE_ALL);\n        array.recycle();\n        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);\n        mScroller = new OverScroller(context, QUNITIC_INTERPOLATOR);\n    }\n\n    @Override\n    protected void onFinishInflate() {\n        super.onFinishInflate();\n        boolean isTargetSet = false;\n        int edgesSet = 0;\n        for (int i = 0; i < getChildCount(); i++) {\n            View view = getChildAt(i);\n            LayoutParams lp = (LayoutParams) view.getLayoutParams();\n            if (lp.isTarget) {\n                if (isTargetSet) {\n                    throw new RuntimeException(\n                            \"More than one view in xml are marked by qmui_is_target = true.\");\n                }\n                isTargetSet = true;\n                setTargetView(view);\n            } else {\n                if ((edgesSet & lp.edge) != 0) {\n                    String text = \"\";\n                    if (lp.edge == PULL_EDGE_LEFT) {\n                        text = \"left\";\n                    } else if (lp.edge == PULL_EDGE_TOP) {\n                        text = \"top\";\n                    } else if (lp.edge == PULL_EDGE_RIGHT) {\n                        text = \"right\";\n                    } else if (lp.edge == PULL_EDGE_BOTTOM) {\n                        text = \"bottom\";\n                    }\n                    throw new RuntimeException(\"More than one view in xml marked by qmui_layout_edge = \" + text);\n                }\n                edgesSet |= lp.edge;\n                setActionView(view, lp);\n            }\n        }\n    }\n\n    @Override\n    public void computeScroll() {\n        if (mScroller.computeScrollOffset()) {\n            if (mScroller.isFinished()) {\n                if(mState == STATE_SETTLING_TO_INIT_OFFSET){\n                    mState = STATE_IDLE;\n                    return;\n                }\n                if(mState == STATE_TRIGGERING){\n                    return;\n                }\n\n                if(mState == STATE_SETTLING_FLING){\n                    checkScrollToTargetOffsetOrInitOffset(false);\n                    return;\n                }\n\n                if(mState == STATE_SETTLING_TO_TRIGGER_OFFSET){\n                    mState = STATE_TRIGGERING;\n                    if (mLeftPullAction != null && isEdgeEnabled(PULL_EDGE_LEFT)) {\n                        if (mScroller.getFinalX() == mLeftPullAction.getTargetTriggerOffset()) {\n                            onActionTriggered(mLeftPullAction);\n                        }\n                    }\n                    if (mRightPullAction != null && isEdgeEnabled(PULL_EDGE_RIGHT)) {\n                        if (mScroller.getFinalX() == -mRightPullAction.getTargetTriggerOffset()) {\n                            onActionTriggered(mRightPullAction);\n                        }\n                    }\n\n                    if (mTopPullAction != null && isEdgeEnabled(PULL_EDGE_TOP)) {\n                        if (mScroller.getFinalY() == mTopPullAction.getTargetTriggerOffset()) {\n                            onActionTriggered(mTopPullAction);\n                        }\n                    }\n                    if (mBottomPullAction != null && isEdgeEnabled(PULL_EDGE_BOTTOM)) {\n                        if (mScroller.getFinalY() == -mBottomPullAction.getTargetTriggerOffset()) {\n                            onActionTriggered(mBottomPullAction);\n                        }\n                    }\n                    setHorOffsetToTargetOffsetHelper(mScroller.getCurrX());\n                    setVerOffsetToTargetOffsetHelper(mScroller.getCurrY());\n                }\n            }else{\n                setHorOffsetToTargetOffsetHelper(mScroller.getCurrX());\n                setVerOffsetToTargetOffsetHelper(mScroller.getCurrY());\n                postInvalidateOnAnimation();\n            }\n        }\n    }\n\n    public void setStopTargetViewFlingImpl(@NonNull StopTargetViewFlingImpl stopTargetViewFlingImpl) {\n        mStopTargetViewFlingImpl = stopTargetViewFlingImpl;\n    }\n\n    public void setMinScrollDuration(int minScrollDuration) {\n        mMinScrollDuration = minScrollDuration;\n    }\n\n    public void setTargetView(@NonNull View view) {\n        if (view.getParent() != this) {\n            throw new RuntimeException(\"Target already exists other parent view.\");\n        }\n        if (view.getParent() == null) {\n            LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            addView(view, lp);\n        }\n        innerSetTargetView(view);\n    }\n\n    private void innerSetTargetView(@NonNull View view) {\n        mTargetView = view;\n        mTargetOffsetHelper = new QMUIViewOffsetHelper(view);\n    }\n\n    public void setActionView(View view, LayoutParams lp) {\n        PullActionBuilder builder = new PullActionBuilder(view, lp.edge)\n                .canOverPull(lp.canOverPull)\n                .pullRate(lp.pullRate)\n                .needReceiveFlingFromTargetView(lp.needReceiveFlingFromTarget)\n                .receivedFlingFraction(lp.receivedFlingFraction)\n                .scrollSpeedPerPixel(lp.scrollSpeedPerPixel)\n                .targetTriggerOffset(lp.targetTriggerOffset)\n                .triggerUntilScrollToTriggerOffset(lp.triggerUntilScrollToTriggerOffset)\n                .scrollToTriggerOffsetAfterTouchUp(lp.scrollToTriggerOffsetAfterTouchUp)\n                .actionInitOffset(lp.actionInitOffset);\n        view.setLayoutParams(lp);\n        setActionView(builder);\n    }\n\n    public void setActionView(@NonNull PullActionBuilder builder) {\n        if (builder.mActionView.getParent() != this) {\n            throw new RuntimeException(\"Action view already exists other parent view.\");\n        }\n        if (builder.mActionView.getParent() == null) {\n            ViewGroup.LayoutParams lp = builder.mActionView.getLayoutParams();\n            if (lp == null) {\n                lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n            addView(builder.mActionView, lp);\n        }\n        if (builder.mPullEdge == PULL_EDGE_LEFT) {\n            mLeftPullAction = builder.build();\n        } else if (builder.mPullEdge == PULL_EDGE_TOP) {\n            mTopPullAction = builder.build();\n        } else if (builder.mPullEdge == PULL_EDGE_RIGHT) {\n            mRightPullAction = builder.build();\n        } else if (builder.mPullEdge == PULL_EDGE_BOTTOM) {\n            mBottomPullAction = builder.build();\n        }\n    }\n\n    public void setActionListener(ActionListener actionListener) {\n        mActionListener = actionListener;\n    }\n\n    public void setEnabledEdges(int enabledEdges) {\n        mEnabledEdges = enabledEdges;\n    }\n\n    public boolean isEdgeEnabled(@PullEdge int edge) {\n        return (mEnabledEdges & edge) == edge && getPullAction(edge) != null;\n    }\n\n    @Nullable\n    private PullAction getPullAction(@PullEdge int edge) {\n        if (edge == PULL_EDGE_LEFT) {\n            return mLeftPullAction;\n        } else if (edge == PULL_EDGE_TOP) {\n            return mTopPullAction;\n        } else if (edge == PULL_EDGE_RIGHT) {\n            return mRightPullAction;\n        } else if (edge == PULL_EDGE_BOTTOM) {\n            return mBottomPullAction;\n        }\n        return null;\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        int w = r - l;\n        int h = b - t;\n        if (mTargetView != null) {\n            mTargetView.layout(0, 0, w, h);\n            mTargetOffsetHelper.onViewLayout();\n        }\n\n        if (mLeftPullAction != null) {\n            View view = mLeftPullAction.mActionView;\n            int vw = view.getMeasuredWidth(), vh = view.getMeasuredHeight(), vc = (h - vh) / 2;\n            view.layout(-vw, vc, 0, vc + vh);\n            mLeftPullAction.mViewOffsetHelper.onViewLayout();\n        }\n\n        if (mTopPullAction != null) {\n            View view = mTopPullAction.mActionView;\n            int vw = view.getMeasuredWidth(), vh = view.getMeasuredHeight(), vc = (w - vw) / 2;\n            view.layout(vc, -vh, vc + vw, 0);\n            mTopPullAction.mViewOffsetHelper.onViewLayout();\n        }\n\n        if (mRightPullAction != null) {\n            View view = mRightPullAction.mActionView;\n            int vw = view.getMeasuredWidth(), vh = view.getMeasuredHeight(), vc = (h - vh) / 2;\n            view.layout(w, vc, w + vw, vc + vh);\n            mRightPullAction.mViewOffsetHelper.onViewLayout();\n        }\n\n        if (mBottomPullAction != null) {\n            View view = mBottomPullAction.mActionView;\n            int vw = view.getMeasuredWidth(), vh = view.getMeasuredHeight(), vc = (w - vw) / 2;\n            view.layout(vc, h, vc + vw, h + vh);\n            mBottomPullAction.mViewOffsetHelper.onViewLayout();\n        }\n    }\n\n    public void setNestedPreFlingVelocityScaleDown(float nestedPreFlingVelocityScaleDown) {\n        mNestedPreFlingVelocityScaleDown = nestedPreFlingVelocityScaleDown;\n    }\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {\n        return mTargetView == target && (axes == ViewCompat.SCROLL_AXIS_HORIZONTAL && (isEdgeEnabled(PULL_EDGE_LEFT) || isEdgeEnabled(PULL_EDGE_RIGHT))) ||\n                (axes == ViewCompat.SCROLL_AXIS_VERTICAL && (isEdgeEnabled(PULL_EDGE_TOP) || isEdgeEnabled(PULL_EDGE_BOTTOM)));\n    }\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {\n        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {\n        if(type == ViewCompat.TYPE_TOUCH){\n            removeStopTargetFlingRunnable();\n            mScroller.abortAnimation();\n            mState = STATE_PULLING;\n        }\n        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);\n    }\n\n    @Override\n    public void onNestedScrollAccepted(View child, View target, int axes) {\n        onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);\n    }\n\n\n    @Override\n    public void onNestedPreScroll(@NonNull final View target, int dx, int dy, @NonNull int[] consumed, int type) {\n        int originDx = dx, originDy = dy;\n        dy = checkEdgeTopScrollDown(dy, consumed, type);\n        dy = checkEdgeBottomScrollDown(dy, consumed, type);\n        dy = checkEdgeTopScrollUp(dy, consumed, type);\n        dy = checkEdgeBottomScrollUp(dy, consumed, type);\n\n        dx = checkEdgeLeftScrollRight(dx, consumed, type);\n        dx = checkEdgeRightScrollRight(dx, consumed, type);\n        dx = checkEdgeLeftScrollLeft(dx, consumed, type);\n        dx = checkEdgeRightScrollLeft(dx, consumed, type);\n\n        if(originDx == dx && originDy == dy && mState == STATE_SETTLING_DELIVER){\n            checkStopTargetFling(target, dx, dy, type);\n        }\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {\n        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,\n                               int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {\n        int originDxUnconsumed = dxUnconsumed, originDyUnconsumed = dyUnconsumed;\n        dyUnconsumed = checkEdgeTopScrollDown(dyUnconsumed, consumed, type);\n        dyUnconsumed = checkEdgeBottomScrollDown(dyUnconsumed, consumed, type);\n        dyUnconsumed = checkEdgeTopScrollUp(dyUnconsumed, consumed, type);\n        dyUnconsumed = checkEdgeBottomScrollUp(dyUnconsumed, consumed, type);\n\n        dxUnconsumed = checkEdgeLeftScrollRight(dxUnconsumed, consumed, type);\n        dxUnconsumed = checkEdgeRightScrollRight(dxUnconsumed, consumed, type);\n        dxUnconsumed = checkEdgeLeftScrollLeft(dxUnconsumed, consumed, type);\n        dxUnconsumed = checkEdgeRightScrollLeft(dxUnconsumed, consumed, type);\n        if(dyUnconsumed == originDyUnconsumed && dxUnconsumed == originDxUnconsumed && mState == STATE_SETTLING_DELIVER){\n            checkStopTargetFling(target, dxUnconsumed, dyUnconsumed, type);\n        }\n    }\n\n    @Override\n    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {\n        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, mNestedScrollingV2ConsumedCompat);\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {\n        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, ViewCompat.TYPE_TOUCH);\n    }\n\n    @Override\n    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n\n        // if the targetView is RecyclerView and we set OnFlingListener for RecyclerView.\n        // then the targetView can not deliver fling consume to NestedScrollParent\n        // so we intercept the fling if the target view can not consume the fling.\n        if(mLeftPullAction != null && isEdgeEnabled(PULL_EDGE_LEFT)){\n            if(velocityX  < 0 && !mTargetView.canScrollHorizontally(-1)){\n                mState = STATE_SETTLING_FLING;\n                velocityX /= mNestedPreFlingVelocityScaleDown;\n                int maxX = mLeftPullAction.isCanOverPull() ? Integer.MAX_VALUE : mLeftPullAction.getTargetTriggerOffset();\n                mScroller.fling(hOffset, vOffset, (int) -velocityX, 0, 0,  maxX, vOffset, vOffset);\n                postInvalidateOnAnimation();\n                return true;\n            }else if(velocityX > 0 && hOffset > 0){\n                mState = STATE_SETTLING_TO_INIT_OFFSET;\n                mScroller.startScroll(hOffset, vOffset, -hOffset, 0, scrollDuration(mLeftPullAction,hOffset));\n                postInvalidateOnAnimation();\n                return true;\n            }\n        }\n\n        if(mRightPullAction != null && isEdgeEnabled(PULL_EDGE_RIGHT)){\n            if(velocityX > 0 && !mTargetView.canScrollHorizontally(1)){\n                mState = STATE_SETTLING_FLING;\n                velocityX /= mNestedPreFlingVelocityScaleDown;\n                int minX = mRightPullAction.isCanOverPull() ? Integer.MIN_VALUE : -mRightPullAction.getTargetTriggerOffset();\n                mScroller.fling(hOffset, vOffset, (int) -velocityX, 0,  minX, 0, vOffset, vOffset);\n                postInvalidateOnAnimation();\n                return true;\n            }else if(velocityX < 0 && hOffset < 0){\n                mState = STATE_SETTLING_TO_INIT_OFFSET;\n                mScroller.startScroll(hOffset, vOffset, -hOffset, 0, scrollDuration(mRightPullAction, hOffset));\n                postInvalidateOnAnimation();\n                return true;\n            }\n        }\n\n        if(mTopPullAction != null && isEdgeEnabled(PULL_EDGE_TOP)){\n            if(velocityY  < 0 && !mTargetView.canScrollVertically(-1)){\n                mState = STATE_SETTLING_FLING;\n                velocityY /= mNestedPreFlingVelocityScaleDown;\n                int maxY = mTopPullAction.isCanOverPull() ? Integer.MAX_VALUE : mTopPullAction.getTargetTriggerOffset();\n                mScroller.fling(hOffset, vOffset, 0, (int) -velocityY, hOffset,  hOffset, 0, maxY);\n                postInvalidateOnAnimation();\n                return true;\n            }else if(velocityY > 0 && vOffset > 0){\n                mState = STATE_SETTLING_TO_INIT_OFFSET;\n                mScroller.startScroll(hOffset, vOffset, 0, -vOffset, scrollDuration(mTopPullAction, vOffset));\n                postInvalidateOnAnimation();\n                return true;\n            }\n        }\n\n        if(mBottomPullAction != null && isEdgeEnabled(PULL_EDGE_BOTTOM)){\n            if(velocityY > 0 && !mTargetView.canScrollVertically(1)){\n                mState = STATE_SETTLING_FLING;\n                velocityY /= mNestedPreFlingVelocityScaleDown;\n                int minY =  mBottomPullAction.isCanOverPull() ? Integer.MIN_VALUE : -mBottomPullAction.getTargetTriggerOffset();\n                mScroller.fling(hOffset, vOffset, 0, (int) -velocityY, hOffset, hOffset,  minY, 0);\n                postInvalidateOnAnimation();\n                return true;\n            }else if(velocityY < 0 && vOffset < 0){\n                mState = STATE_SETTLING_TO_INIT_OFFSET;\n                mScroller.startScroll(hOffset, vOffset, 0, -vOffset, scrollDuration(mBottomPullAction, vOffset));\n                postInvalidateOnAnimation();\n                return true;\n            }\n        }\n        mState = STATE_SETTLING_DELIVER;\n        return super.onNestedPreFling(target, velocityX, velocityY);\n    }\n\n    @Override\n    public void onStopNestedScroll(@NonNull View target, int type) {\n        if(mState == STATE_PULLING){\n            checkScrollToTargetOffsetOrInitOffset(false);\n        }else if(mState == STATE_SETTLING_DELIVER && type != ViewCompat.TYPE_TOUCH){\n            removeStopTargetFlingRunnable();\n            checkScrollToTargetOffsetOrInitOffset(false);\n        }\n    }\n\n    private int scrollDuration(PullAction pullAction, int delta){\n        return Math.max(mMinScrollDuration, Math.abs((int) (pullAction.mScrollSpeedPerPixel * delta)));\n    }\n\n    private void onActionTriggered(PullAction pullAction) {\n        if(pullAction.mIsActionRunning){\n            return;\n        }\n        pullAction.mIsActionRunning = true;\n        if(mActionListener != null){\n            mActionListener.onActionTriggered(pullAction);\n        }\n        if(pullAction.mActionView instanceof ActionPullWatcherView){\n            ((ActionPullWatcherView)pullAction.mActionView).onActionTriggered();\n        }\n    }\n\n    public void finishActionRun(@NonNull PullAction pullAction){\n        finishActionRun(pullAction, true);\n    }\n\n    public void finishActionRun(@NonNull PullAction pullAction, boolean animate){\n        if(pullAction != getPullAction(pullAction.mPullEdge)){\n            return;\n        }\n        pullAction.mIsActionRunning = false;\n        if(pullAction.mActionView instanceof ActionPullWatcherView){\n            ((ActionPullWatcherView)pullAction.mActionView).onActionFinished();\n        }\n        if(mState == STATE_PULLING){\n            return;\n        }\n        if(!animate){\n            mState = STATE_IDLE;\n            setVerOffsetToTargetOffsetHelper(0);\n            setHorOffsetToTargetOffsetHelper(0);\n            return;\n        }\n        mState = STATE_SETTLING_TO_INIT_OFFSET;\n        @PullEdge int pullEdge = pullAction.getPullEdge();\n        int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        if(pullEdge == PULL_EDGE_TOP && mTopPullAction != null && vOffset > 0){\n            mScroller.startScroll(hOffset, vOffset, 0, -vOffset, scrollDuration(mTopPullAction, vOffset));\n            postInvalidateOnAnimation();\n        }else if(pullEdge == PULL_EDGE_BOTTOM && mBottomPullAction != null && vOffset < 0){\n            mScroller.startScroll(hOffset, vOffset, 0, -vOffset, scrollDuration(mBottomPullAction, vOffset));\n            postInvalidateOnAnimation();\n        }else if(pullEdge == PULL_EDGE_LEFT && mLeftPullAction != null && hOffset > 0){\n            mScroller.startScroll(hOffset, vOffset, -hOffset, 0, scrollDuration(mLeftPullAction, hOffset));\n            postInvalidateOnAnimation();\n        }else if(pullEdge == PULL_EDGE_RIGHT && mRightPullAction != null && hOffset < 0){\n            mScroller.startScroll(hOffset, vOffset, -hOffset, 0, scrollDuration(mRightPullAction, hOffset));\n            postInvalidateOnAnimation();\n        }\n    }\n\n    private void checkScrollToTargetOffsetOrInitOffset(boolean forceInit) {\n        if (mTargetView == null) {\n            return;\n        }\n        mScroller.abortAnimation();\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n        int hTarget = 0, vTarget = 0;\n        if (mLeftPullAction != null && isEdgeEnabled(PULL_EDGE_LEFT) && hOffset > 0) {\n            mState = STATE_SETTLING_TO_INIT_OFFSET;\n            if(!forceInit){\n                int targetOffset = mLeftPullAction.getTargetTriggerOffset();\n                if(hOffset == targetOffset){\n                    onActionTriggered(mLeftPullAction);\n                    return;\n                }\n                if(hOffset > targetOffset){\n                    if(!mLeftPullAction.mScrollToTriggerOffsetAfterTouchUp){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mLeftPullAction);\n                        return;\n                    }\n                    if(!mLeftPullAction.mTriggerUntilScrollToTriggerOffset){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mLeftPullAction);\n                    }else{\n                        mState = STATE_SETTLING_TO_TRIGGER_OFFSET;\n                    }\n                    hTarget = targetOffset;\n                }\n            }\n            int dx = hTarget - hOffset;\n            mScroller.startScroll(hOffset, vOffset, dx, 0, scrollDuration(mLeftPullAction, dx));\n            postInvalidateOnAnimation();\n            return;\n        }\n\n        if(mRightPullAction != null && isEdgeEnabled(PULL_EDGE_RIGHT) && hOffset < 0){\n            mState = STATE_SETTLING_TO_INIT_OFFSET;\n            if(!forceInit){\n                int targetOffset = mRightPullAction.getTargetTriggerOffset();\n                if (hOffset == -targetOffset) {\n                    mState = STATE_TRIGGERING;\n                    onActionTriggered(mRightPullAction);\n                    return;\n                }\n                if(hOffset < -targetOffset){\n                    if(!mRightPullAction.mScrollToTriggerOffsetAfterTouchUp){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mRightPullAction);\n                        return;\n                    }\n\n                    if(!mRightPullAction.mTriggerUntilScrollToTriggerOffset){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mRightPullAction);\n                    }else{\n                        mState = STATE_SETTLING_TO_TRIGGER_OFFSET;\n                    }\n                    hTarget = -targetOffset;\n                }\n            }\n            int dx = hTarget - hOffset;\n            mScroller.startScroll(hOffset, vOffset, dx, 0,scrollDuration(mRightPullAction, dx));\n            postInvalidateOnAnimation();\n            return;\n        }\n\n        if (mTopPullAction != null && isEdgeEnabled(PULL_EDGE_TOP) && vOffset > 0) {\n            mState = STATE_SETTLING_TO_INIT_OFFSET;\n            if(!forceInit){\n                int targetOffset = mTopPullAction.getTargetTriggerOffset();\n                if(vOffset == targetOffset){\n                    mState = STATE_TRIGGERING;\n                    onActionTriggered(mTopPullAction);\n                    return;\n                }\n                if(vOffset > targetOffset){\n                    if(!mTopPullAction.mScrollToTriggerOffsetAfterTouchUp){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mTopPullAction);\n                        return;\n                    }\n\n                    if(!mTopPullAction.mTriggerUntilScrollToTriggerOffset){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mTopPullAction);\n                    }else{\n                        mState = STATE_SETTLING_TO_TRIGGER_OFFSET;\n                    }\n                    vTarget = targetOffset;\n                }\n            }\n            int dy = vTarget - vOffset;\n            mScroller.startScroll(hOffset, vOffset, hOffset, dy, scrollDuration(mTopPullAction, dy));\n            postInvalidateOnAnimation();\n            return;\n        }\n\n        if (mBottomPullAction != null && isEdgeEnabled(PULL_EDGE_BOTTOM) && vOffset < 0) {\n            mState = STATE_SETTLING_TO_INIT_OFFSET;\n            if(!forceInit){\n                int targetOffset = mBottomPullAction.getTargetTriggerOffset();\n                if(vOffset == -targetOffset){\n                    onActionTriggered(mBottomPullAction);\n                    return;\n                }\n                if(vOffset < -targetOffset){\n                    if(!mBottomPullAction.mScrollToTriggerOffsetAfterTouchUp){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mBottomPullAction);\n                        return;\n                    }\n\n                    if(!mBottomPullAction.mTriggerUntilScrollToTriggerOffset){\n                        mState = STATE_TRIGGERING;\n                        onActionTriggered(mBottomPullAction);\n                    }else{\n                        mState = STATE_SETTLING_TO_TRIGGER_OFFSET;\n                    }\n                    vTarget = -targetOffset;\n                }\n            }\n            int dy = vTarget - vOffset;\n            mScroller.startScroll(hOffset, vOffset, hOffset, dy, scrollDuration(mBottomPullAction, dy));\n            postInvalidateOnAnimation();\n            return;\n        }\n\n        mState = STATE_IDLE;\n    }\n\n    private void removeStopTargetFlingRunnable() {\n        if (mStopTargetFlingRunnable != null) {\n            removeCallbacks(mStopTargetFlingRunnable);\n            mStopTargetFlingRunnable = null;\n        }\n    }\n\n    private void checkStopTargetFling(final View targetView, int dx, int dy, int type) {\n        if (mStopTargetFlingRunnable != null || type == ViewCompat.TYPE_TOUCH) {\n            return;\n        }\n        if ((dy < 0 && !mTargetView.canScrollVertically(-1)) ||\n                (dy > 0 && !mTargetView.canScrollVertically(1)) ||\n                (dx < 0 && !mTargetView.canScrollHorizontally(-1)) ||\n                (dx > 0 && !mTargetView.canScrollHorizontally(1))) {\n            mStopTargetFlingRunnable = new Runnable() {\n                @Override\n                public void run() {\n                    mStopTargetViewFlingImpl.stopFling(targetView);\n                    mStopTargetFlingRunnable = null;\n                    checkScrollToTargetOffsetOrInitOffset(false);\n                }\n            };\n            post(mStopTargetFlingRunnable);\n        }\n    }\n\n    private void setHorOffsetToTargetOffsetHelper(int hOffset) {\n        mTargetOffsetHelper.setLeftAndRightOffset(hOffset);\n        onTargetViewLeftAndRightOffsetChanged(hOffset);\n        if (mLeftPullAction != null) {\n            mLeftPullAction.onTargetMoved(hOffset);\n            if(mLeftPullAction.mActionView instanceof ActionPullWatcherView){\n                ((ActionPullWatcherView)mLeftPullAction.mActionView).onPull(mLeftPullAction, hOffset);\n            }\n\n        }\n        if (mRightPullAction != null) {\n            mRightPullAction.onTargetMoved(-hOffset);\n            if(mRightPullAction.mActionView instanceof ActionPullWatcherView){\n                ((ActionPullWatcherView)mRightPullAction.mActionView).onPull(mRightPullAction, -hOffset);\n            }\n        }\n    }\n\n    private void setVerOffsetToTargetOffsetHelper(int vOffset) {\n        mTargetOffsetHelper.setTopAndBottomOffset(vOffset);\n        onTargetViewTopAndBottomOffsetChanged(vOffset);\n        if (mTopPullAction != null) {\n            mTopPullAction.onTargetMoved(vOffset);\n            if(mTopPullAction.mActionView instanceof ActionPullWatcherView){\n                ((ActionPullWatcherView)mTopPullAction.mActionView).onPull(mTopPullAction, vOffset);\n            }\n\n        }\n        if (mBottomPullAction != null) {\n            mBottomPullAction.onTargetMoved(-vOffset);\n            if(mBottomPullAction.mActionView instanceof ActionPullWatcherView){\n                ((ActionPullWatcherView)mBottomPullAction.mActionView).onPull(mBottomPullAction, -vOffset);\n            }\n        }\n    }\n\n    protected void onTargetViewTopAndBottomOffsetChanged(int vOffset){\n\n    }\n\n    protected void onTargetViewLeftAndRightOffsetChanged(int hOffset){\n\n    }\n\n    private int checkEdgeTopScrollDown(int dy, int[] consumed, int type) {\n        int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n        if (dy > 0 && isEdgeEnabled(PULL_EDGE_TOP) && vOffset > 0) {\n            float pullRate = type == ViewCompat.TYPE_TOUCH ?  mTopPullAction.getPullRate() : 1f;\n            int ry = (int) (dy * pullRate);\n            if(ry == 0){\n                return dy;\n            }\n            if (vOffset >= ry) {\n                consumed[1] += dy;\n                vOffset -= ry;\n                dy = 0;\n            } else {\n                int yConsumed = (int) (vOffset / pullRate);\n                consumed[1] += yConsumed;\n                dy -= yConsumed;\n                vOffset = 0;\n            }\n            setVerOffsetToTargetOffsetHelper(vOffset);\n        }\n        return dy;\n    }\n\n    private int checkEdgeTopScrollUp(int dy, int[] consumed, int type) {\n        if (dy < 0 && isEdgeEnabled(PULL_EDGE_TOP) && !mTargetView.canScrollVertically(-1) &&\n                (type == ViewCompat.TYPE_TOUCH || mTopPullAction.mNeedReceiveFlingFromTargetView)) {\n            int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n            float pullRate = type == ViewCompat.TYPE_TOUCH ? mTopPullAction.getPullRate(): mTopPullAction.getFlingRate(vOffset);\n            int ry = (int) (dy * pullRate);\n            if(ry == 0){\n                return dy;\n            }\n            if (mTopPullAction.mCanOverPull || -ry <= mTopPullAction.getTargetTriggerOffset() - vOffset) {\n                vOffset -= ry;\n                consumed[1] += dy;\n                dy = 0;\n            } else {\n                int yConsumed = (int) ((vOffset - mTopPullAction.getTargetTriggerOffset()) / pullRate);\n                consumed[1] += yConsumed;\n                dy -= yConsumed;\n                vOffset = mBottomPullAction.getTargetTriggerOffset();\n            }\n            setVerOffsetToTargetOffsetHelper(vOffset);\n        }\n        return dy;\n    }\n\n    private int checkEdgeBottomScrollDown(int dy, int[] consumed, int type) {\n        if (dy > 0 && isEdgeEnabled(PULL_EDGE_BOTTOM) && !mTargetView.canScrollVertically(1) &&\n                (type == ViewCompat.TYPE_TOUCH || mBottomPullAction.mNeedReceiveFlingFromTargetView)) {\n            int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n            float pullRate =type == ViewCompat.TYPE_TOUCH ? mBottomPullAction.getPullRate(): mBottomPullAction.getFlingRate(-vOffset);\n            int ry = (int) (dy * pullRate);\n            if(ry == 0){\n                return dy;\n            }\n            if (mBottomPullAction.mCanOverPull || vOffset - ry >= -mBottomPullAction.getTargetTriggerOffset()) {\n                vOffset -= ry;\n                consumed[1] += dy;\n                dy = 0;\n            } else {\n                int yConsumed = (int) ((-mBottomPullAction.getTargetTriggerOffset() - vOffset) / pullRate);\n                consumed[1] += yConsumed;\n                dy -= yConsumed;\n                vOffset = -mBottomPullAction.getTargetTriggerOffset();\n            }\n            setVerOffsetToTargetOffsetHelper(vOffset);\n        }\n        return dy;\n    }\n\n    private int checkEdgeBottomScrollUp(int dy, int[] consumed, int type) {\n        int vOffset = mTargetOffsetHelper.getTopAndBottomOffset();\n        if (dy < 0 && isEdgeEnabled(PULL_EDGE_BOTTOM) && vOffset < 0) {\n            float pullRate = type == ViewCompat.TYPE_TOUCH ?  mBottomPullAction.getPullRate() : 1f;\n            int ry = (int) (dy * pullRate);\n            if(ry == 0){\n                return dy;\n            }\n            if (vOffset <= ry) {\n                consumed[1] += dy;\n                vOffset -= ry;\n                dy = 0;\n            } else {\n                int yConsumed = (int) (vOffset / pullRate);\n                consumed[1] += yConsumed;\n                dy -= yConsumed;\n                vOffset = 0;\n            }\n            setVerOffsetToTargetOffsetHelper(vOffset);\n        }\n        return dy;\n    }\n\n    private int checkEdgeLeftScrollRight(int dx, int[] consumed, int type) {\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        if (dx > 0 && isEdgeEnabled(PULL_EDGE_LEFT) && hOffset > 0) {\n            float pullRate = type == ViewCompat.TYPE_TOUCH ? mLeftPullAction.getPullRate(): 1f;\n            int rx = (int) (dx * pullRate);\n            if(rx == 0){\n                return dx;\n            }\n            if (hOffset >= rx) {\n                consumed[0] += dx;\n                hOffset -= rx;\n                dx = 0;\n            } else {\n                int xConsumed = (int) (hOffset / pullRate);\n                consumed[0] += xConsumed;\n                dx -= xConsumed;\n                hOffset = 0;\n            }\n            setHorOffsetToTargetOffsetHelper(hOffset);\n        }\n        return dx;\n    }\n\n    private int checkEdgeLeftScrollLeft(int dx, int[] consumed, int type) {\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        if (dx < 0 && isEdgeEnabled(PULL_EDGE_LEFT) && !mTargetView.canScrollHorizontally(-1) &&\n                (type == ViewCompat.TYPE_TOUCH || mLeftPullAction.mNeedReceiveFlingFromTargetView)) {\n            float pullRate =type == ViewCompat.TYPE_TOUCH ? mLeftPullAction.getPullRate(): mLeftPullAction.getFlingRate(hOffset);\n            int rx = (int) (dx * pullRate);\n            if(rx == 0){\n                return dx;\n            }\n            if (mLeftPullAction.mCanOverPull || -rx <= mLeftPullAction.getTargetTriggerOffset() - hOffset) {\n                hOffset -= rx;\n                consumed[0] += dx;\n                dx = 0;\n            } else {\n                int xConsumed = (int) ((hOffset - mLeftPullAction.getTargetTriggerOffset()) / pullRate);\n                consumed[0] += xConsumed;\n                dx -= xConsumed;\n                hOffset = mLeftPullAction.getTargetTriggerOffset();\n            }\n            setHorOffsetToTargetOffsetHelper(hOffset);\n        }\n        return dx;\n    }\n\n    private int checkEdgeRightScrollRight(int dx, int[] consumed, int type) {\n        if (dx > 0 && isEdgeEnabled(PULL_EDGE_RIGHT) && !mTargetView.canScrollHorizontally(1) &&\n                (type == ViewCompat.TYPE_TOUCH || mRightPullAction.mNeedReceiveFlingFromTargetView)) {\n            int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n            float pullRate = type == ViewCompat.TYPE_TOUCH ? mRightPullAction.getPullRate(): mRightPullAction.getFlingRate(-hOffset);\n            int rx = (int) (dx * pullRate);\n            if(rx == 0){\n                return dx;\n            }\n            if (mRightPullAction.mCanOverPull || hOffset - rx >= -mRightPullAction.getTargetTriggerOffset()) {\n                hOffset -= rx;\n                consumed[0] += dx;\n                dx = 0;\n            } else {\n                int xConsumed = (int) ((-mRightPullAction.getTargetTriggerOffset() - hOffset) / pullRate);\n                consumed[0] += xConsumed;\n                dx -= xConsumed;\n                hOffset = -mRightPullAction.getTargetTriggerOffset();\n            }\n            setHorOffsetToTargetOffsetHelper(hOffset);\n        }\n        return dx;\n    }\n\n    private int checkEdgeRightScrollLeft(int dx, int[] consumed, int type) {\n        int hOffset = mTargetOffsetHelper.getLeftAndRightOffset();\n        if (dx < 0 && isEdgeEnabled(PULL_EDGE_RIGHT) && hOffset < 0) {\n            float pullRate = type == ViewCompat.TYPE_TOUCH ? mRightPullAction.getPullRate(): 1f;\n            int rx = (int) (dx * pullRate);\n            if(rx == 0){\n                return dx;\n            }\n            if (hOffset <= dx) {\n                consumed[0] += dx;\n                hOffset -= rx;\n                dx = 0;\n            } else {\n                int xConsumed = (int) (hOffset / pullRate);\n                consumed[0] += xConsumed;\n                dx -= xConsumed;\n                hOffset = 0;\n            }\n            setHorOffsetToTargetOffsetHelper(hOffset);\n        }\n        return dx;\n    }\n\n    @Override\n    protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {\n        return new LayoutParams(lp);\n    }\n\n    @Override\n    public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {\n        return new LayoutParams(getContext(), attrs);\n    }\n\n    @Override\n    protected FrameLayout.LayoutParams generateDefaultLayoutParams() {\n        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    @Override\n    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n        return p instanceof LayoutParams && super.checkLayoutParams(p);\n    }\n\n    public static class LayoutParams extends FrameLayout.LayoutParams {\n        public boolean isTarget = false;\n        public int edge = PULL_EDGE_TOP;\n        public int targetTriggerOffset = ViewGroup.LayoutParams.WRAP_CONTENT;\n        public boolean canOverPull = false;\n        public float pullRate = DEFAULT_PULL_RATE;\n        public boolean needReceiveFlingFromTarget = true;\n        public float receivedFlingFraction = DEFAULT_FLING_FRACTION;\n        public int actionInitOffset = 0;\n        public float scrollSpeedPerPixel = DEFAULT_SCROLL_SPEED_PER_PIXEL;\n        public boolean triggerUntilScrollToTriggerOffset = false;\n        public boolean scrollToTriggerOffsetAfterTouchUp = true;\n\n\n        public LayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n            final TypedArray a = c.obtainStyledAttributes(attrs,\n                    R.styleable.QMUIPullLayout_Layout);\n            isTarget = a.getBoolean(R.styleable.QMUIPullLayout_Layout_qmui_is_target, false);\n            if (!isTarget) {\n                edge = a.getInteger(R.styleable.QMUIPullLayout_Layout_qmui_pull_edge, PULL_EDGE_TOP);\n                try {\n                    targetTriggerOffset = a.getDimensionPixelSize(\n                            R.styleable.QMUIPullLayout_Layout_qmui_target_view_trigger_offset,\n                            ViewGroup.LayoutParams.WRAP_CONTENT);\n                } catch (Exception ignore) {\n                    int intValue = a.getInt(R.styleable.QMUIPullLayout_Layout_qmui_target_view_trigger_offset, ViewGroup.LayoutParams.WRAP_CONTENT);\n                    if (intValue == ViewGroup.LayoutParams.WRAP_CONTENT) {\n                        targetTriggerOffset = ViewGroup.LayoutParams.WRAP_CONTENT;\n                    }\n                }\n\n                canOverPull = a.getBoolean(\n                        R.styleable.QMUIPullLayout_Layout_qmui_can_over_pull, false);\n                pullRate = a.getFloat(\n                        R.styleable.QMUIPullLayout_Layout_qmui_pull_rate, pullRate);\n                needReceiveFlingFromTarget = a.getBoolean(\n                        R.styleable.QMUIPullLayout_Layout_qmui_need_receive_fling_from_target_view, true);\n                receivedFlingFraction = a.getFloat(\n                        R.styleable.QMUIPullLayout_Layout_qmui_received_fling_fraction, receivedFlingFraction);\n                actionInitOffset = a.getDimensionPixelSize(R.styleable.QMUIPullLayout_Layout_qmui_action_view_init_offset, 0);\n                scrollSpeedPerPixel = a.getFloat(R.styleable.QMUIPullLayout_Layout_qmui_scroll_speed_per_pixel, scrollSpeedPerPixel);\n                triggerUntilScrollToTriggerOffset = a.getBoolean(R.styleable.QMUIPullLayout_Layout_qmui_trigger_until_scroll_to_trigger_offset, false);\n                scrollToTriggerOffsetAfterTouchUp = a.getBoolean(R.styleable.QMUIPullLayout_Layout_qmui_scroll_to_trigger_offset_after_touch_up, true);\n            }\n            a.recycle();\n        }\n\n        public LayoutParams(int width, int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(ViewGroup.LayoutParams p) {\n            super(p);\n        }\n\n        public LayoutParams(MarginLayoutParams source) {\n            super(source);\n        }\n    }\n\n    public final static class PullAction {\n        @NonNull\n        private final View mActionView;\n        private final int mTargetTriggerOffset;\n        private final boolean mCanOverPull;\n        private final float mPullRate;\n        private final float mReceivedFlingFraction;\n        private final int mActionInitOffset;\n        @PullEdge\n        private final int mPullEdge;\n        private final float mScrollSpeedPerPixel;\n        private final boolean mNeedReceiveFlingFromTargetView;\n        private final boolean mTriggerUntilScrollToTriggerOffset;\n        private final boolean mScrollToTriggerOffsetAfterTouchUp;\n        private final QMUIViewOffsetHelper mViewOffsetHelper;\n        private final ActionViewOffsetCalculator mActionViewOffsetCalculator;\n\n        private boolean mIsActionRunning = false;\n\n        PullAction(@NonNull View actionView,\n                   int targetOffset,\n                   boolean isTargetCanOverPull,\n                   float targetPullRate,\n                   int actionInitOffset,\n                   @PullEdge int pullEdge,\n                   float scrollSpeedPerPixel,\n                   boolean needReceiveFlingFromTargetView,\n                   float receivedFlingFraction,\n                   boolean triggerUntilScrollToTriggerOffset,\n                   boolean scrollToTriggerOffsetAfterTouchUp,\n                   ActionViewOffsetCalculator calculator) {\n            mActionView = actionView;\n            mTargetTriggerOffset = targetOffset;\n            mCanOverPull = isTargetCanOverPull;\n            mPullRate = targetPullRate;\n            mNeedReceiveFlingFromTargetView = needReceiveFlingFromTargetView;\n            mReceivedFlingFraction = receivedFlingFraction;\n            mActionInitOffset = actionInitOffset;\n            mScrollSpeedPerPixel = scrollSpeedPerPixel;\n            mPullEdge = pullEdge;\n            mTriggerUntilScrollToTriggerOffset = triggerUntilScrollToTriggerOffset;\n            mScrollToTriggerOffsetAfterTouchUp = scrollToTriggerOffsetAfterTouchUp;\n            mActionViewOffsetCalculator = calculator;\n\n            mViewOffsetHelper = new QMUIViewOffsetHelper(actionView);\n            updateOffset(actionInitOffset);\n        }\n\n        public int getActionPullSize() {\n            if (mPullEdge == PULL_EDGE_TOP || mPullEdge == PULL_EDGE_BOTTOM) {\n                return mActionView.getHeight();\n            }\n            return mActionView.getWidth();\n        }\n\n        public int getActionInitOffset() {\n            return mActionInitOffset;\n        }\n\n        public int getTargetTriggerOffset() {\n            if (mTargetTriggerOffset == ViewGroup.LayoutParams.WRAP_CONTENT) {\n                return getActionPullSize() - getActionInitOffset() * 2;\n            }\n            return mTargetTriggerOffset;\n        }\n\n        public float getScrollSpeedPerPixel() {\n            return mScrollSpeedPerPixel;\n        }\n\n        public float getPullRate() {\n            return mPullRate;\n        }\n\n        public boolean isNeedReceiveFlingFromTargetView() {\n            return mNeedReceiveFlingFromTargetView;\n        }\n\n        public boolean isScrollToTriggerOffsetAfterTouchUp() {\n            return mScrollToTriggerOffsetAfterTouchUp;\n        }\n\n        public boolean isTriggerUntilScrollToTriggerOffset() {\n            return mTriggerUntilScrollToTriggerOffset;\n        }\n\n        public float getFlingRate(int currentTargetOffset){\n            return Math.min(mPullRate, Math.max(mPullRate - (currentTargetOffset - getTargetTriggerOffset()) * mReceivedFlingFraction, 0));\n        }\n\n        public boolean isCanOverPull() {\n            return mCanOverPull;\n        }\n\n        @PullEdge\n        public int getPullEdge() {\n            return mPullEdge;\n        }\n\n        void updateOffset(int offset) {\n            if (mPullEdge == PULL_EDGE_LEFT) {\n                mViewOffsetHelper.setLeftAndRightOffset(offset);\n            } else if (mPullEdge == PULL_EDGE_TOP) {\n                mViewOffsetHelper.setTopAndBottomOffset(offset);\n            } else if (mPullEdge == PULL_EDGE_RIGHT) {\n                mViewOffsetHelper.setLeftAndRightOffset(-offset);\n            } else {\n                mViewOffsetHelper.setTopAndBottomOffset(-offset);\n            }\n        }\n\n        void onTargetMoved(int targetOffset) {\n            updateOffset(\n                    mActionViewOffsetCalculator.calculateOffset(this, targetOffset));\n        }\n    }\n\n    public static class PullActionBuilder {\n        @NonNull\n        private final View mActionView;\n        private int mTargetTriggerOffset = ViewGroup.LayoutParams.WRAP_CONTENT;\n        private boolean mCanOverPull;\n        private float mPullRate = DEFAULT_PULL_RATE;\n        private boolean mNeedReceiveFlingFromTargetView = true;\n        private float mReceivedFlingFraction = DEFAULT_FLING_FRACTION;\n        private int mActionInitOffset;\n        private float mScrollSpeedPerPixel = DEFAULT_SCROLL_SPEED_PER_PIXEL;\n        @PullEdge\n        private int mPullEdge;\n        private ActionViewOffsetCalculator mActionViewOffsetCalculator;\n        private boolean mTriggerUntilScrollToTriggerOffset = false;\n        private boolean mScrollToTriggerOffsetAfterTouchUp = true;\n\n        public PullActionBuilder(@NonNull View actionView, @PullEdge int pullEdge) {\n            mActionView = actionView;\n            mPullEdge = pullEdge;\n        }\n\n        public PullActionBuilder triggerUntilScrollToTriggerOffset(boolean triggerUntilScrollToTriggerOffset){\n            mTriggerUntilScrollToTriggerOffset = triggerUntilScrollToTriggerOffset;\n            return this;\n        }\n\n        public PullActionBuilder scrollToTriggerOffsetAfterTouchUp(boolean scrollToTriggerOffsetAfterTouchUp){\n            mScrollToTriggerOffsetAfterTouchUp = scrollToTriggerOffsetAfterTouchUp;\n            return this;\n        }\n\n        public PullActionBuilder targetTriggerOffset(int offset) {\n            mTargetTriggerOffset = offset;\n            return this;\n        }\n\n        public PullActionBuilder canOverPull(boolean canOverPull) {\n            mCanOverPull = canOverPull;\n            return this;\n        }\n\n        public PullActionBuilder receivedFlingFraction(float fraction) {\n            mReceivedFlingFraction = fraction;\n            return this;\n        }\n\n        public PullActionBuilder needReceiveFlingFromTargetView(boolean needReceive) {\n            mNeedReceiveFlingFromTargetView = needReceive;\n            return this;\n        }\n\n        public PullActionBuilder pullRate(float rate){\n            mPullRate = rate;\n            return this;\n        }\n\n        public PullActionBuilder scrollSpeedPerPixel(float scrollSpeedPerPixel){\n            mScrollSpeedPerPixel = scrollSpeedPerPixel;\n            return this;\n        }\n\n        public PullActionBuilder actionInitOffset(int initOffset) {\n            mActionInitOffset = initOffset;\n            return this;\n        }\n\n        public PullActionBuilder actionViewOffsetCalculator(ActionViewOffsetCalculator calculator) {\n            mActionViewOffsetCalculator = calculator;\n            return this;\n        }\n\n\n        PullAction build() {\n            if (mActionViewOffsetCalculator == null) {\n                mActionViewOffsetCalculator = new QMUIAlwaysFollowOffsetCalculator();\n            }\n            return new PullAction(mActionView,\n                    mTargetTriggerOffset,\n                    mCanOverPull,\n                    mPullRate,\n                    mActionInitOffset,\n                    mPullEdge,\n                    mScrollSpeedPerPixel,\n                    mNeedReceiveFlingFromTargetView,\n                    mReceivedFlingFraction,\n                    mTriggerUntilScrollToTriggerOffset,\n                    mScrollToTriggerOffsetAfterTouchUp,\n                    mActionViewOffsetCalculator);\n        }\n    }\n\n    public interface ActionViewOffsetCalculator {\n        int calculateOffset(PullAction pullAction, int targetOffset);\n    }\n\n    public interface  ActionPullWatcherView {\n        void onPull(PullAction pullAction, int currentTargetOffset);\n        void onActionTriggered();\n        void onActionFinished();\n    }\n\n    public interface StopTargetViewFlingImpl {\n        void stopFling(View view);\n    }\n\n    public static class DefaultStopTargetViewFlingImpl implements StopTargetViewFlingImpl {\n\n        private static DefaultStopTargetViewFlingImpl sInstance;\n\n        public static DefaultStopTargetViewFlingImpl getInstance() {\n            if (sInstance == null) {\n                sInstance = new DefaultStopTargetViewFlingImpl();\n            }\n            return sInstance;\n        }\n\n        private DefaultStopTargetViewFlingImpl() {\n\n        }\n\n        @Override\n        public void stopFling(View view) {\n            if (view instanceof RecyclerView) {\n                ((RecyclerView) view).stopScroll();\n            }\n        }\n    }\n\n    public interface ActionListener {\n        void onActionTriggered(@NonNull PullAction pullAction);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUIPullLoadMoreView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.widget.ImageViewCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUILoadingView;\n\npublic class QMUIPullLoadMoreView extends ConstraintLayout implements QMUIPullLayout.ActionPullWatcherView {\n\n    private boolean mIsLoading = false;\n    private QMUILoadingView mLoadingView;\n    private AppCompatImageView mArrowView;\n    private AppCompatTextView mTextView;\n    private int mHeight;\n    private String mPullText;\n    private String mReleaseText;\n    private boolean mIsInReleaseState = false;\n\n    public QMUIPullLoadMoreView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIPullLoadMoreView(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIPullLoadMoreStyle);\n\n    }\n\n    public QMUIPullLoadMoreView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUIPullLoadMoreView, defStyleAttr, 0);\n        mPullText = array.getString(R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_pull_text);\n        mReleaseText = array.getString(R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_release_text);\n        mHeight = array.getDimensionPixelSize(\n                R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_height,\n                QMUIDisplayHelper.dp2px(context, 56));\n        int loadSize = array.getDimensionPixelSize(\n                R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_loading_size,\n                QMUIDisplayHelper.dp2px(context, 20));\n        int textSize = array.getDimensionPixelSize(\n                R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_text_size,\n                QMUIDisplayHelper.sp2px(context, 14));\n        int arrowTextGap =  array.getDimensionPixelSize(\n                R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_arrow_text_gap,\n                QMUIDisplayHelper.dp2px(context, 10));\n        Drawable arrow = array.getDrawable(R.styleable.QMUIPullLoadMoreView_qmui_pull_load_more_arrow);\n        int bgColor = array.getColor(\n                R.styleable.QMUIPullLoadMoreView_qmui_skin_support_pull_load_more_bg_color,\n                Color.TRANSPARENT);\n        int loadingTintColor = array.getColor(\n                R.styleable.QMUIPullLoadMoreView_qmui_skin_support_pull_load_more_loading_tint_color,\n                Color.BLACK);\n        int arrowTintColor = array.getColor(\n                R.styleable.QMUIPullLoadMoreView_qmui_skin_support_pull_load_more_arrow_tint_color,\n                Color.BLACK);\n        int textColor = array.getColor(\n                R.styleable.QMUIPullLoadMoreView_qmui_skin_support_pull_load_more_text_color,\n                Color.BLACK);\n        array.recycle();\n\n        mLoadingView = new QMUILoadingView(context);\n        mLoadingView.setSize(loadSize);\n        mLoadingView.setColor(loadingTintColor);\n        mLoadingView.setVisibility(View.GONE);\n        LayoutParams lp = new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft = LayoutParams.PARENT_ID;\n        lp.rightToRight = LayoutParams.PARENT_ID;\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        addView(mLoadingView, lp);\n\n        mArrowView = new AppCompatImageView(context);\n        mArrowView.setId(View.generateViewId());\n        mArrowView.setImageDrawable(arrow);\n        mArrowView.setRotation(180);\n        ImageViewCompat.setImageTintList(mArrowView, ColorStateList.valueOf(arrowTintColor));\n\n        mTextView = new AppCompatTextView(context);\n        mTextView.setId(View.generateViewId());\n        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);\n        mTextView.setTextColor(textColor);\n        mTextView.setText(mPullText);\n\n        lp = new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToLeft =  LayoutParams.PARENT_ID;\n        lp.rightToLeft = mTextView.getId();\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        lp.horizontalChainStyle = LayoutParams.CHAIN_PACKED;\n        addView(mArrowView, lp);\n\n        lp = new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.leftToRight = mArrowView.getId();\n        lp.rightToRight = LayoutParams.PARENT_ID;\n        lp.topToTop = LayoutParams.PARENT_ID;\n        lp.bottomToBottom = LayoutParams.PARENT_ID;\n        lp.leftMargin = arrowTextGap;\n        addView(mTextView, lp);\n        setBackgroundColor(bgColor);\n\n\n        QMUISkinValueBuilder skinValueBuilder = QMUISkinValueBuilder.acquire();\n        skinValueBuilder.background(R.attr.qmui_skin_support_pull_load_more_bg_color);\n        QMUISkinHelper.setSkinValue(this, skinValueBuilder);\n\n        skinValueBuilder.clear();\n        skinValueBuilder.tintColor(R.attr.qmui_skin_support_pull_load_more_loading_tint_color);\n        QMUISkinHelper.setSkinValue(mLoadingView, skinValueBuilder);\n\n        skinValueBuilder.clear();\n        skinValueBuilder.tintColor(R.attr.qmui_skin_support_pull_load_more_arrow_tint_color);\n        QMUISkinHelper.setSkinValue(mArrowView, skinValueBuilder);\n\n        skinValueBuilder.clear();\n        skinValueBuilder.textColor(R.attr.qmui_skin_support_pull_load_more_text_color);\n        QMUISkinHelper.setSkinValue(mTextView, skinValueBuilder);\n\n        skinValueBuilder.release();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));\n    }\n\n    @Override\n    public void onPull(QMUIPullLayout.PullAction pullAction, int currentTargetOffset) {\n        if(mIsLoading){\n            return;\n        }\n        if(mIsInReleaseState){\n            if(pullAction.getTargetTriggerOffset() > currentTargetOffset){\n                mIsInReleaseState = false;\n                mTextView.setText(mPullText);\n                mArrowView.animate().rotation(180).start();\n            }\n        }else{\n            if(pullAction.getTargetTriggerOffset() <= currentTargetOffset){\n                mIsInReleaseState = true;\n                mTextView.setText(mReleaseText);\n                mArrowView.animate().rotation(0).start();\n            }\n        }\n\n    }\n\n    @Override\n    public void onActionTriggered() {\n        mIsLoading = true;\n        mLoadingView.setVisibility(View.VISIBLE);\n        mLoadingView.start();\n        mArrowView.setVisibility(View.GONE);\n        mTextView.setVisibility(View.GONE);\n    }\n\n    @Override\n    public void onActionFinished() {\n        mIsLoading = false;\n        mLoadingView.stop();\n        mLoadingView.setVisibility(View.GONE);\n        mArrowView.setVisibility(View.VISIBLE);\n        mTextView.setVisibility(View.VISIBLE);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullLayout/QMUIPullRefreshView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullLayout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.ColorRes;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.core.content.ContextCompat;\nimport androidx.swiperefreshlayout.widget.CircularProgressDrawable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\npublic class QMUIPullRefreshView extends AppCompatImageView implements QMUIPullLayout.ActionPullWatcherView, IQMUISkinDefaultAttrProvider {\n    private static final int MAX_ALPHA = 255;\n    private static final float TRIM_RATE = 0.85f;\n    private static final float TRIM_OFFSET = 0.4f;\n\n    static final int CIRCLE_DIAMETER = 40;\n    static final int CIRCLE_DIAMETER_LARGE = 56;\n\n    private CircularProgressDrawable mProgress;\n    private int mCircleDiameter;\n\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(4);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.TINT_COLOR, R.attr.qmui_skin_support_pull_refresh_view_color);\n    }\n\n    public QMUIPullRefreshView(Context context) {\n        this(context, null);\n    }\n\n    public QMUIPullRefreshView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        mProgress = new CircularProgressDrawable(context);\n        setColorSchemeColors(QMUIResHelper.getAttrColor(\n                context, R.attr.qmui_skin_support_pull_refresh_view_color));\n        mProgress.setStyle(CircularProgressDrawable.LARGE);\n        mProgress.setAlpha(MAX_ALPHA);\n        mProgress.setArrowScale(0.8f);\n        setImageDrawable(mProgress);\n        final DisplayMetrics metrics = getResources().getDisplayMetrics();\n        mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        setMeasuredDimension(mCircleDiameter, mCircleDiameter);\n    }\n\n    @Override\n    public void onActionTriggered() {\n        mProgress.start();\n    }\n\n    @Override\n    public void onActionFinished() {\n        mProgress.stop();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mProgress.stop();\n    }\n\n    @Override\n    public void onPull(QMUIPullLayout.PullAction pullAction, int currentTargetOffset) {\n        if (mProgress.isRunning()) {\n            return;\n        }\n        int targetOffset = pullAction.getTargetTriggerOffset();\n        float end = TRIM_RATE * Math.min(targetOffset, currentTargetOffset) / targetOffset;\n        float rotate = TRIM_OFFSET * currentTargetOffset / targetOffset;\n        mProgress.setArrowEnabled(true);\n        mProgress.setStartEndTrim(0, end);\n        mProgress.setProgressRotation(rotate);\n    }\n\n    public void setSize(@CircularProgressDrawable.ProgressDrawableSize int size) {\n        if (size != CircularProgressDrawable.LARGE && size != CircularProgressDrawable.DEFAULT) {\n            return;\n        }\n        final DisplayMetrics metrics = getResources().getDisplayMetrics();\n        if (size == CircularProgressDrawable.LARGE) {\n            mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);\n        } else {\n            mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);\n        }\n        // force the bounds of the progress circle inside the circle view to\n        // update by setting it to null before updating its size and then\n        // re-setting it\n        setImageDrawable(null);\n        mProgress.setStyle(size);\n        setImageDrawable(mProgress);\n    }\n\n    public void stop() {\n        mProgress.stop();\n    }\n\n    public void doRefresh() {\n\n    }\n\n    public void setColorSchemeResources(@ColorRes int... colorResIds) {\n        final Context context = getContext();\n        int[] colorRes = new int[colorResIds.length];\n        for (int i = 0; i < colorResIds.length; i++) {\n            colorRes[i] = ContextCompat.getColor(context, colorResIds[i]);\n        }\n        setColorSchemeColors(colorRes);\n    }\n\n    public void setColorSchemeColors(@ColorInt int... colors) {\n        mProgress.setColorSchemeColors(colors);\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullRefreshLayout/QMUICenterGravityRefreshOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullRefreshLayout;\n\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout.RefreshOffsetCalculator;\n\n/**\n * 当targetCurrentOffset < refreshViewHeight, refreshView跟随targetView，其距离为0\n * 当targetCurrentOffset >= targetRefreshOffset RefreshView垂直方向永远居中于在[0, targetCurrentOffset]\n *\n * @author cginechen\n * @date 2017-06-07\n */\n\npublic class QMUICenterGravityRefreshOffsetCalculator implements RefreshOffsetCalculator {\n\n    @Override\n    public int calculateRefreshOffset(int refreshInitOffset, int refreshEndOffset, int refreshViewHeight, int targetCurrentOffset, int targetInitOffset, int targetRefreshOffset) {\n        if(targetCurrentOffset < refreshViewHeight){\n            return targetCurrentOffset - refreshViewHeight;\n        }\n        return (targetCurrentOffset - refreshViewHeight) / 2;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullRefreshLayout/QMUIDefaultRefreshOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullRefreshLayout;\n\n/**\n * {@link QMUIPullRefreshLayout}的默认RefreshView偏移量计算器：\n * 偏移范围限定在[refreshInitOffset, refreshEndOffset]\n *\n * @author cginechen\n * @date 2017-06-07\n */\n\npublic class QMUIDefaultRefreshOffsetCalculator implements QMUIPullRefreshLayout.RefreshOffsetCalculator {\n\n    @Override\n    public int calculateRefreshOffset(int refreshInitOffset, int refreshEndOffset, int refreshViewHeight, int targetCurrentOffset, int targetInitOffset, int targetRefreshOffset) {\n        int refreshOffset;\n        if (targetCurrentOffset >= targetRefreshOffset) {\n            refreshOffset = refreshEndOffset;\n        } else if (targetCurrentOffset <= targetInitOffset) {\n            refreshOffset = refreshInitOffset;\n        } else {\n            float percent = (targetCurrentOffset - targetInitOffset) * 1.0f / (targetRefreshOffset - targetInitOffset);\n            refreshOffset = (int) (refreshInitOffset + percent * (refreshEndOffset - refreshInitOffset));\n        }\n        return refreshOffset;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullRefreshLayout/QMUIFollowRefreshOffsetCalculator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullRefreshLayout;\n\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout.RefreshOffsetCalculator;\n\n/**\n * RefreshView 永远和 TargetView 保持一定的距离(这个距离由刷新时RefreshView居中算出)\n *\n * @author cginechen\n * @date 2017-06-07\n */\n\npublic class QMUIFollowRefreshOffsetCalculator implements RefreshOffsetCalculator {\n\n    @Override\n    public int calculateRefreshOffset(int refreshInitOffset, int refreshEndOffset, int refreshViewHeight, int targetCurrentOffset, int targetInitOffset, int targetRefreshOffset) {\n        int distance = targetRefreshOffset / 2 + refreshViewHeight / 2;\n        int max = targetCurrentOffset - refreshViewHeight;\n        return Math.min(max, targetCurrentOffset - distance);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/pullRefreshLayout/QMUIPullRefreshLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.pullRefreshLayout;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.Scroller;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.ColorRes;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.NestedScrollingParent;\nimport androidx.core.view.NestedScrollingParentHelper;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.swiperefreshlayout.widget.CircularProgressDrawable;\n\nimport com.qmuiteam.qmui.QMUIConfig;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionLayout;\n\n/**\n * 下拉刷新控件, 作为容器，下拉时会将子 View 下移, 并拉出 RefreshView（表示正在刷新的 View）\n * <ul>\n * <li>可通过继承并覆写 {@link #createRefreshView()} 方法实现自己的 RefreshView</li>\n * <li>可通过 {@link #setRefreshOffsetCalculator(RefreshOffsetCalculator)} 自己决定在下拉过程中 RefreshView 的位置</li>\n * <li>可在 xml 中使用 {@link com.qmuiteam.qmui.R.styleable#QMUIPullRefreshLayout} 这些属性或在 Java 设置对应的属性决定子View的开始位置、触发刷新的位置等值</li>\n * </ul>\n *\n * @author cginechen\n * @date 2016-12-11\n */\npublic class QMUIPullRefreshLayout extends ViewGroup implements NestedScrollingParent {\n    private static final String TAG = \"QMUIPullRefreshLayout\";\n    private static final int INVALID_POINTER = -1;\n    private static final int FLAG_NEED_SCROLL_TO_INIT_POSITION = 1;\n    private static final int FLAG_NEED_SCROLL_TO_REFRESH_POSITION = 1 << 1;\n    private static final int FLAG_NEED_DO_REFRESH = 1 << 2;\n    private static final int FLAG_NEED_DELIVER_VELOCITY = 1 << 3;\n    private final NestedScrollingParentHelper mNestedScrollingParentHelper;\n    boolean mIsRefreshing = false;\n    private View mTargetView;\n    private IRefreshView mIRefreshView;\n    private View mRefreshView;\n    private int mRefreshZIndex = -1;\n    private int mSystemTouchSlop;\n    private int mTouchSlop;\n    private OnPullListener mListener;\n    private OnChildScrollUpCallback mChildScrollUpCallback;\n    /**\n     * RefreshView的初始offset\n     */\n    private int mRefreshInitOffset;\n    /**\n     * 刷新时RefreshView的offset\n     */\n    private int mRefreshEndOffset;\n    /**\n     * RefreshView当前offset\n     */\n    private int mRefreshCurrentOffset;\n    /**\n     * 是否自动根据RefreshView的高度计算RefreshView的初始位置\n     */\n    private boolean mAutoCalculateRefreshInitOffset = true;\n    /**\n     * 是否自动根据TargetView在刷新时的位置计算RefreshView的结束位置\n     */\n    private boolean mAutoCalculateRefreshEndOffset = true;\n    /**\n     * 自动让TargetView的刷新位置与RefreshView高度相等\n     */\n    private boolean mEqualTargetRefreshOffsetToRefreshViewHeight = false;\n    /**\n     * 当拖拽超过超过mAutoScrollToRefreshMinOffset时，自动滚动到刷新位置并触发刷新\n     * mAutoScrollToRefreshMinOffset == - 1表示要mAutoScrollToRefreshMinOffset>=mTargetRefreshOffset\n     */\n    private int mAutoScrollToRefreshMinOffset = -1;\n    /**\n     * TargetView(ListView或者ScrollView等)的初始位置\n     */\n    private int mTargetInitOffset;\n    /**\n     * 下拉时 TargetView（ListView 或者 ScrollView 等）当前的位置。\n     */\n    private int mTargetCurrentOffset;\n    /**\n     * 刷新时TargetView(ListView或者ScrollView等)的位置\n     */\n    private int mTargetRefreshOffset;\n    private boolean mDisableNestScrollImpl = false;\n    private boolean mEnableOverPull = true;\n    private boolean mNestedScrollInProgress;\n    private int mActivePointerId = INVALID_POINTER;\n    private boolean mIsDragging;\n    private float mInitialDownY;\n    private float mInitialDownX;\n    @SuppressWarnings(\"FieldCanBeLocal\") private float mInitialMotionY;\n    private float mLastMotionY;\n    private float mDragRate = 0.65f;\n    private RefreshOffsetCalculator mRefreshOffsetCalculator;\n    private VelocityTracker mVelocityTracker;\n    private float mMaxVelocity;\n    private float mMiniVelocity;\n    private Scroller mScroller;\n    private int mScrollFlag = 0;\n    private boolean mNestScrollDurationRefreshing = false;\n    private Runnable mPendingRefreshDirectlyAction = null;\n    private boolean mSafeDisallowInterceptTouchEvent = false;\n\n\n    public QMUIPullRefreshLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIPullRefreshLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUIPullRefreshLayoutStyle);\n    }\n\n    public QMUIPullRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setWillNotDraw(false);\n\n        final ViewConfiguration vc = ViewConfiguration.get(context);\n        mMaxVelocity = vc.getScaledMaximumFlingVelocity();\n        mMiniVelocity = vc.getScaledMinimumFlingVelocity();\n        mSystemTouchSlop = vc.getScaledTouchSlop();\n        mTouchSlop = QMUIDisplayHelper.px2dp(context, mSystemTouchSlop); //系统的值是8dp,如何配置？\n\n        mScroller = new Scroller(getContext());\n        mScroller.setFriction(getScrollerFriction());\n\n        addRefreshView();\n        ViewCompat.setChildrenDrawingOrderEnabled(this, true);\n\n        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);\n\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUIPullRefreshLayout, defStyleAttr, 0);\n\n        try {\n            mRefreshInitOffset = array.getDimensionPixelSize(\n                    R.styleable.QMUIPullRefreshLayout_qmui_refresh_init_offset, Integer.MIN_VALUE);\n            mRefreshEndOffset = array.getDimensionPixelSize(\n                    R.styleable.QMUIPullRefreshLayout_qmui_refresh_end_offset, Integer.MIN_VALUE);\n            mTargetInitOffset = array.getDimensionPixelSize(\n                    R.styleable.QMUIPullRefreshLayout_qmui_target_init_offset, 0);\n            mTargetRefreshOffset = array.getDimensionPixelSize(\n                    R.styleable.QMUIPullRefreshLayout_qmui_target_refresh_offset,\n                    QMUIDisplayHelper.dp2px(getContext(), 72));\n            mAutoCalculateRefreshInitOffset = mRefreshInitOffset == Integer.MIN_VALUE ||\n                    array.getBoolean(R.styleable.QMUIPullRefreshLayout_qmui_auto_calculate_refresh_init_offset, false);\n            mAutoCalculateRefreshEndOffset = mRefreshEndOffset == Integer.MIN_VALUE ||\n                    array.getBoolean(R.styleable.QMUIPullRefreshLayout_qmui_auto_calculate_refresh_end_offset, false);\n            mEqualTargetRefreshOffsetToRefreshViewHeight = array.getBoolean(R.styleable.QMUIPullRefreshLayout_qmui_equal_target_refresh_offset_to_refresh_view_height, false);\n        } finally {\n            array.recycle();\n        }\n        mRefreshCurrentOffset = mRefreshInitOffset;\n        mTargetCurrentOffset = mTargetInitOffset;\n    }\n\n    public static boolean defaultCanScrollUp(View view) {\n        if (view == null) {\n            return false;\n        }\n        if (view instanceof QMUIContinuousNestedScrollLayout) {\n            QMUIContinuousNestedScrollLayout layout = (QMUIContinuousNestedScrollLayout) view;\n            return layout.getCurrentScroll() > 0;\n        }\n\n        if (view instanceof QMUIStickySectionLayout) {\n            QMUIStickySectionLayout layout = (QMUIStickySectionLayout) view;\n            return defaultCanScrollUp(layout.getRecyclerView());\n        }\n\n        if (android.os.Build.VERSION.SDK_INT < 14) {\n            if (view instanceof AbsListView) {\n                final AbsListView absListView = (AbsListView) view;\n                return absListView.getChildCount() > 0\n                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)\n                        .getTop() < absListView.getPaddingTop());\n            } else {\n                return ViewCompat.canScrollVertically(view, -1) || view.getScrollY() > 0;\n            }\n        } else {\n            return ViewCompat.canScrollVertically(view, -1);\n        }\n    }\n\n    public void setOnPullListener(OnPullListener listener) {\n        mListener = listener;\n    }\n\n    public void setDisableNestScrollImpl(boolean disableNestScrollImpl) {\n        mDisableNestScrollImpl = disableNestScrollImpl;\n    }\n\n    public void setDragRate(float dragRate) {\n        // have no idea to change drag rate for nest scroll\n        mDisableNestScrollImpl = true;\n        mDragRate = dragRate;\n    }\n\n    public void setChildScrollUpCallback(OnChildScrollUpCallback childScrollUpCallback) {\n        mChildScrollUpCallback = childScrollUpCallback;\n    }\n\n    protected float getScrollerFriction() {\n        return ViewConfiguration.getScrollFriction();\n    }\n\n    public void setAutoScrollToRefreshMinOffset(int autoScrollToRefreshMinOffset) {\n        mAutoScrollToRefreshMinOffset = autoScrollToRefreshMinOffset;\n    }\n\n    public boolean isRefreshing() {\n        return mIsRefreshing;\n    }\n\n    /**\n     * 覆盖该方法以实现自己的 RefreshView。\n     *\n     * @return 自定义的 RefreshView, 注意该 View 必须实现 {@link IRefreshView} 接口\n     */\n    protected View createRefreshView() {\n        return new RefreshView(getContext());\n    }\n\n    private void addRefreshView() {\n        if (mRefreshView == null) {\n            mRefreshView = createRefreshView();\n        }\n        if (!(mRefreshView instanceof IRefreshView)) {\n            throw new RuntimeException(\"refreshView must be a instance of IRefreshView\");\n        }\n        mIRefreshView = (IRefreshView) mRefreshView;\n        if (mRefreshView.getLayoutParams() == null) {\n            mRefreshView.setLayoutParams(new LayoutParams(\n                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n        }\n        addView(mRefreshView);\n    }\n\n    /**\n     * 设置在下拉过程中 RefreshView 的偏移量\n     */\n    public void setRefreshOffsetCalculator(RefreshOffsetCalculator refreshOffsetCalculator) {\n        mRefreshOffsetCalculator = refreshOffsetCalculator;\n    }\n\n    @Override\n    protected int getChildDrawingOrder(int childCount, int i) {\n        if (mRefreshZIndex < 0) {\n            return i;\n        }\n        // 最后才绘制mRefreshView\n        if (i == mRefreshZIndex) {\n            return childCount - 1;\n        }\n        if (i > mRefreshZIndex) {\n            return i - 1;\n        }\n        return i;\n    }\n\n    /**\n     * child view call, to ensure disallowInterceptTouchEvent make sense\n     * <p>\n     * how to optimize this...\n     */\n    public void openSafeDisallowInterceptTouchEvent() {\n        mSafeDisallowInterceptTouchEvent = true;\n    }\n\n    @Override\n    public void requestDisallowInterceptTouchEvent(boolean b) {\n\n        if (mSafeDisallowInterceptTouchEvent) {\n            super.requestDisallowInterceptTouchEvent(b);\n            mSafeDisallowInterceptTouchEvent = false;\n        }\n\n        // if this is a List < L or another view that doesn't support nested\n        // scrolling, ignore this request so that the vertical scroll event\n        // isn't stolen\n        //noinspection StatementWithEmptyBody\n        if ((android.os.Build.VERSION.SDK_INT < 21 && mTargetView instanceof AbsListView)\n                || (mTargetView != null && !ViewCompat.isNestedScrollingEnabled(mTargetView))) {\n            // Nope.\n        } else {\n            super.requestDisallowInterceptTouchEvent(b);\n        }\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        int targetMeasureWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - getPaddingLeft() - getPaddingRight() , MeasureSpec.EXACTLY);\n        int targetMeasureHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);\n        measureChild(mRefreshView, widthMeasureSpec, heightMeasureSpec);\n        int refreshViewHeight = mRefreshView.getMeasuredHeight();\n        if (mAutoCalculateRefreshInitOffset) {\n            if (mRefreshInitOffset != -refreshViewHeight) {\n                mRefreshInitOffset = -refreshViewHeight;\n                mRefreshCurrentOffset = mRefreshInitOffset;\n            }\n\n        }\n        if (mEqualTargetRefreshOffsetToRefreshViewHeight) {\n            mTargetRefreshOffset = refreshViewHeight;\n        }\n        if (mAutoCalculateRefreshEndOffset) {\n            mRefreshEndOffset = (mTargetRefreshOffset - refreshViewHeight) / 2;\n        }\n\n        mRefreshZIndex = -1;\n        for (int i = 0; i < getChildCount(); i++) {\n            if (getChildAt(i) == mRefreshView) {\n                mRefreshZIndex = i;\n                break;\n            }\n        }\n\n\n        ensureTargetView();\n        if (mTargetView == null) {\n            Log.d(TAG, \"onMeasure: mTargetView == null\");\n            setMeasuredDimension(widthSize, heightSize);\n            return;\n        }\n        mTargetView.measure(targetMeasureWidthSpec, targetMeasureHeightSpec);\n        setMeasuredDimension(widthSize, heightSize);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        final int width = getMeasuredWidth();\n        final int height = getMeasuredHeight();\n        if (getChildCount() == 0) {\n            return;\n        }\n        ensureTargetView();\n        if (mTargetView == null) {\n            Log.d(TAG, \"onLayout: mTargetView == null\");\n            return;\n        }\n\n        final int childLeft = getPaddingLeft();\n        final int childTop = getPaddingTop();\n        final int childWidth = width - getPaddingLeft() - getPaddingRight();\n        final int childHeight = height - getPaddingTop() - getPaddingBottom();\n        mTargetView.layout(childLeft, childTop + mTargetCurrentOffset,\n                childLeft + childWidth, childTop + childHeight + mTargetCurrentOffset);\n        int refreshViewWidth = mRefreshView.getMeasuredWidth();\n        int refreshViewHeight = mRefreshView.getMeasuredHeight();\n        mRefreshView.layout((width / 2 - refreshViewWidth / 2), mRefreshCurrentOffset,\n                (width / 2 + refreshViewWidth / 2), mRefreshCurrentOffset + refreshViewHeight);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        ensureTargetView();\n\n        final int action = ev.getAction();\n        int pointerIndex;\n\n        if (!isEnabled() || canChildScrollUp() || mNestedScrollInProgress) {\n            if (QMUIConfig.DEBUG) {\n                Log.d(TAG, \"fast end onIntercept: isEnabled = \" + isEnabled() + \"; canChildScrollUp = \"\n                        + canChildScrollUp() + \" ; mNestedScrollInProgress = \" + mNestedScrollInProgress);\n            }\n            return false;\n        }\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mIsDragging = false;\n                mActivePointerId = ev.getPointerId(0);\n                pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex < 0) {\n                    return false;\n                }\n                mInitialDownX = ev.getX(pointerIndex);\n                mInitialDownY = ev.getY(pointerIndex);\n                break;\n\n            case MotionEvent.ACTION_MOVE:\n                pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex < 0) {\n                    Log.e(TAG, \"Got ACTION_MOVE event but have an invalid active pointer id.\");\n                    return false;\n                }\n\n                final float x = ev.getX(pointerIndex);\n                final float y = ev.getY(pointerIndex);\n                startDragging(x, y);\n                break;\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onSecondaryPointerUp(ev);\n                break;\n\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIsDragging = false;\n                mActivePointerId = INVALID_POINTER;\n                break;\n        }\n\n        return mIsDragging;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        final int action = ev.getAction();\n        int pointerIndex;\n\n        if (!isEnabled() || canChildScrollUp() || mNestedScrollInProgress) {\n            Log.d(TAG, \"fast end onTouchEvent: isEnabled = \" + isEnabled() + \"; canChildScrollUp = \"\n                    + canChildScrollUp() + \" ; mNestedScrollInProgress = \" + mNestedScrollInProgress);\n            return false;\n        }\n\n        acquireVelocityTracker(ev);\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mIsDragging = false;\n                mScrollFlag = 0;\n                if (!mScroller.isFinished()) {\n                    mScroller.abortAnimation();\n                }\n                mActivePointerId = ev.getPointerId(0);\n                break;\n\n            case MotionEvent.ACTION_MOVE: {\n                pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex < 0) {\n                    Log.e(TAG, \"onTouchEvent Got ACTION_MOVE event but have an invalid active pointer id.\");\n                    return false;\n                }\n                final float x = ev.getX(pointerIndex);\n                final float y = ev.getY(pointerIndex);\n                startDragging(x, y);\n\n                if (mIsDragging) {\n                    float dy = (y - mLastMotionY) * mDragRate;\n                    if (dy >= 0) {\n                        moveTargetView(dy);\n                    } else {\n                        int move = moveTargetView(dy);\n                        float delta = Math.abs(dy) - Math.abs(move);\n                        if (delta > 0) {\n                            // 重新dispatch一次down事件，使得列表可以继续滚动\n                            ev.setAction(MotionEvent.ACTION_DOWN);\n                            // 立刻dispatch一个大于mSystemTouchSlop的move事件，防止触发TargetView\n                            float offsetLoc = mSystemTouchSlop + 1;\n                            if (delta > offsetLoc) {\n                                offsetLoc = delta;\n                            }\n                            ev.offsetLocation(0, offsetLoc);\n                            super.dispatchTouchEvent(ev);\n                            ev.setAction(action);\n                            // 再dispatch一次move事件，消耗掉所有dy\n                            ev.offsetLocation(0, -offsetLoc);\n                            super.dispatchTouchEvent(ev);\n                        }\n                    }\n                    mLastMotionY = y;\n                }\n                break;\n            }\n            case MotionEvent.ACTION_POINTER_DOWN: {\n                pointerIndex = ev.getActionIndex();\n                if (pointerIndex < 0) {\n                    Log.e(TAG, \"Got ACTION_POINTER_DOWN event but have an invalid action index.\");\n                    return false;\n                }\n                mActivePointerId = ev.getPointerId(pointerIndex);\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onSecondaryPointerUp(ev);\n                break;\n\n            case MotionEvent.ACTION_UP: {\n                pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex < 0) {\n                    Log.e(TAG, \"Got ACTION_UP event but don't have an active pointer id.\");\n                    return false;\n                }\n\n                if (mIsDragging) {\n                    mIsDragging = false;\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    float vy = mVelocityTracker.getYVelocity(mActivePointerId);\n                    if (Math.abs(vy) < mMiniVelocity) {\n                        vy = 0;\n                    }\n                    finishPull((int) vy);\n                }\n                mActivePointerId = INVALID_POINTER;\n                releaseVelocityTracker();\n                return false;\n            }\n            case MotionEvent.ACTION_CANCEL:\n                releaseVelocityTracker();\n                return false;\n        }\n\n        return true;\n    }\n\n    private void ensureTargetView() {\n        if (mTargetView == null) {\n            for (int i = 0; i < getChildCount(); i++) {\n                View view = getChildAt(i);\n                if (!view.equals(mRefreshView)) {\n                    onSureTargetView(view);\n                    mTargetView = view;\n                    break;\n                }\n            }\n        }\n        if (mTargetView != null && mPendingRefreshDirectlyAction != null) {\n            Runnable runnable = mPendingRefreshDirectlyAction;\n            mPendingRefreshDirectlyAction = null;\n            runnable.run();\n        }\n    }\n\n    /**\n     * 确定TargetView, 提供机会给子类来做一些初始化的操作\n     */\n    protected void onSureTargetView(View targetView) {\n\n    }\n\n    protected void onFinishPull(int vy, int refreshInitOffset, int refreshEndOffset, int refreshViewHeight,\n                                int targetCurrentOffset, int targetInitOffset, int targetRefreshOffset) {\n\n    }\n\n    private void finishPull(int vy) {\n        info(\"finishPull: vy = \" + vy + \" ; mTargetCurrentOffset = \" + mTargetCurrentOffset +\n                \" ; mTargetRefreshOffset = \" + mTargetRefreshOffset + \" ; mTargetInitOffset = \" + mTargetInitOffset +\n                \" ; mScroller.isFinished() = \" + mScroller.isFinished());\n        int miniVy = vy / 1000; // 向下拖拽时， 速度不能太大\n        onFinishPull(miniVy, mRefreshInitOffset, mRefreshEndOffset, mRefreshView.getMeasuredHeight(),\n                mTargetCurrentOffset, mTargetInitOffset, mTargetRefreshOffset);\n        if (mTargetCurrentOffset >= mTargetRefreshOffset) {\n            if (miniVy > 0) {\n                mScrollFlag = FLAG_NEED_SCROLL_TO_REFRESH_POSITION | FLAG_NEED_DO_REFRESH;\n                mScroller.fling(0, mTargetCurrentOffset, 0, miniVy,\n                        0, 0, mTargetInitOffset, Integer.MAX_VALUE);\n                invalidate();\n            } else if (miniVy < 0) {\n                mScroller.fling(0, mTargetCurrentOffset, 0, vy,\n                        0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);\n                if (mScroller.getFinalY() < mTargetInitOffset) {\n                    mScrollFlag = FLAG_NEED_DELIVER_VELOCITY;\n                } else if (mScroller.getFinalY() < mTargetRefreshOffset) {\n                    int dy = mTargetInitOffset - mTargetCurrentOffset;\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, dy);\n                } else if (mScroller.getFinalY() == mTargetRefreshOffset) {\n                    mScrollFlag = FLAG_NEED_DO_REFRESH;\n                } else {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetRefreshOffset - mTargetCurrentOffset);\n                    mScrollFlag = FLAG_NEED_DO_REFRESH;\n                }\n                invalidate();\n            } else {\n                if (mTargetCurrentOffset > mTargetRefreshOffset) {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetRefreshOffset - mTargetCurrentOffset);\n                }\n                mScrollFlag = FLAG_NEED_DO_REFRESH;\n                invalidate();\n            }\n        } else {\n            if (miniVy > 0) {\n                mScroller.fling(0, mTargetCurrentOffset, 0, miniVy, 0, 0, mTargetInitOffset, Integer.MAX_VALUE);\n                if (mScroller.getFinalY() > mTargetRefreshOffset) {\n                    mScrollFlag = FLAG_NEED_SCROLL_TO_REFRESH_POSITION | FLAG_NEED_DO_REFRESH;\n                } else if (mAutoScrollToRefreshMinOffset >= 0 && mScroller.getFinalY() > mAutoScrollToRefreshMinOffset) {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetRefreshOffset - mTargetCurrentOffset);\n                    mScrollFlag = FLAG_NEED_DO_REFRESH;\n                } else {\n                    mScrollFlag = FLAG_NEED_SCROLL_TO_INIT_POSITION;\n                }\n                invalidate();\n            } else if (miniVy < 0) {\n                mScrollFlag = 0;\n                mScroller.fling(0, mTargetCurrentOffset, 0, vy, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);\n                if (mScroller.getFinalY() < mTargetInitOffset) {\n                    mScrollFlag = FLAG_NEED_DELIVER_VELOCITY;\n                } else {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetInitOffset - mTargetCurrentOffset);\n                    mScrollFlag = 0;\n                }\n                invalidate();\n            } else {\n                if (mTargetCurrentOffset == mTargetInitOffset) {\n                    return;\n                }\n                if (mAutoScrollToRefreshMinOffset >= 0 && mTargetCurrentOffset >= mAutoScrollToRefreshMinOffset) {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetRefreshOffset - mTargetCurrentOffset);\n                    mScrollFlag = FLAG_NEED_DO_REFRESH;\n                } else {\n                    mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetInitOffset - mTargetCurrentOffset);\n                    mScrollFlag = 0;\n                }\n                invalidate();\n            }\n        }\n    }\n\n    protected void onRefresh() {\n        if (mIsRefreshing) {\n            return;\n        }\n        mIsRefreshing = true;\n        mIRefreshView.doRefresh();\n        if (mListener != null) {\n            mListener.onRefresh();\n        }\n    }\n\n    public void finishRefresh() {\n        mIsRefreshing = false;\n        mIRefreshView.stop();\n        mScrollFlag = FLAG_NEED_SCROLL_TO_INIT_POSITION;\n        mScroller.forceFinished(true);\n        invalidate();\n    }\n\n    public void setToRefreshDirectly() {\n        setToRefreshDirectly(0, true);\n    }\n\n    public void setToRefreshDirectly(final long delay){\n        setToRefreshDirectly(delay, true);\n    }\n\n    public void setToRefreshDirectly(final long delay, final boolean animate) {\n        if (mTargetView != null) {\n            Runnable runnable = new Runnable() {\n                @Override\n                public void run() {\n                    setTargetViewToTop(mTargetView);\n                    if(animate){\n                        mScrollFlag = FLAG_NEED_SCROLL_TO_REFRESH_POSITION;\n                        invalidate();\n                    }else{\n                        moveTargetViewTo(mTargetRefreshOffset, true);\n                    }\n                    onRefresh();\n                }\n            };\n            if(delay == 0){\n                runnable.run();\n            }else{\n                postDelayed(runnable, delay);\n            }\n        } else {\n            mPendingRefreshDirectlyAction = new Runnable() {\n                @Override\n                public void run() {\n                    setToRefreshDirectly(delay, animate);\n                }\n            };\n        }\n    }\n\n\n    public void setEnableOverPull(boolean enableOverPull) {\n        mEnableOverPull = enableOverPull;\n    }\n\n    protected void setTargetViewToTop(View targetView) {\n        if (targetView instanceof RecyclerView) {\n            ((RecyclerView) targetView).scrollToPosition(0);\n        } else if (targetView instanceof AbsListView) {\n            AbsListView listView = (AbsListView) targetView;\n            listView.setSelectionFromTop(0, 0);\n        } else {\n            targetView.scrollTo(0, 0);\n        }\n    }\n\n\n    private void onSecondaryPointerUp(MotionEvent ev) {\n        final int pointerIndex = ev.getActionIndex();\n        final int pointerId = ev.getPointerId(pointerIndex);\n        if (pointerId == mActivePointerId) {\n            // This was our active pointer going up. Choose a new\n            // active pointer and adjust accordingly.\n            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n            mActivePointerId = ev.getPointerId(newPointerIndex);\n        }\n    }\n\n    public void reset() {\n        mIRefreshView.stop();\n        mIsRefreshing = false;\n        mScroller.forceFinished(true);\n        mScrollFlag = 0;\n        moveTargetViewTo(mTargetInitOffset);\n    }\n\n    protected void startDragging(float x, float y) {\n        final float dx = x - mInitialDownX;\n        final float dy = y - mInitialDownY;\n        boolean isYDrag = isYDrag(dx, dy);\n        if (isYDrag && (dy > mTouchSlop || (dy < -mTouchSlop && mTargetCurrentOffset > mTargetInitOffset)) && !mIsDragging) {\n            mInitialMotionY = mInitialDownY + mTouchSlop;\n            mLastMotionY = mInitialMotionY;\n            mIsDragging = true;\n        }\n    }\n\n    protected boolean isYDrag(float dx, float dy) {\n        return Math.abs(dy) > Math.abs(dx);\n    }\n\n    public boolean isDragging() {\n        return mIsDragging;\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        reset();\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        super.setEnabled(enabled);\n        if (!enabled) {\n            reset();\n            invalidate();\n        }\n    }\n\n    public boolean canChildScrollUp() {\n        if (mChildScrollUpCallback != null) {\n            return mChildScrollUpCallback.canChildScrollUp(this, mTargetView);\n        }\n        return defaultCanScrollUp(mTargetView);\n    }\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {\n        info(\"onStartNestedScroll: nestedScrollAxes = \" + nestedScrollAxes);\n        return !mDisableNestScrollImpl && isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;\n    }\n\n    @Override\n    public void onNestedScrollAccepted(View child, View target, int axes) {\n        info(\"onNestedScrollAccepted: axes = \" + axes);\n        mScroller.abortAnimation();\n        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);\n        mNestedScrollInProgress = true;\n        mIsDragging = true;\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {\n        info(\"onNestedPreScroll: dx = \" + dx + \" ; dy = \" + dy);\n        int parentCanConsume = mTargetCurrentOffset - mTargetInitOffset;\n        if (dy > 0 && parentCanConsume > 0) {\n            if (dy >= parentCanConsume) {\n                consumed[1] = parentCanConsume;\n                moveTargetViewTo(mTargetInitOffset);\n            } else {\n                consumed[1] = dy;\n                moveTargetView(-dy);\n            }\n        }\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {\n        info(\"onNestedScroll: dxConsumed = \" + dxConsumed + \" ; dyConsumed = \" + dyConsumed +\n                \" ; dxUnconsumed = \" + dxUnconsumed + \" ; dyUnconsumed = \" + dyUnconsumed);\n        if (dyUnconsumed < 0 && !canChildScrollUp() && mScroller.isFinished() && mScrollFlag == 0) {\n            moveTargetView(-dyUnconsumed);\n        }\n    }\n\n    @Override\n    public int getNestedScrollAxes() {\n        return mNestedScrollingParentHelper.getNestedScrollAxes();\n    }\n\n    @Override\n    public void onStopNestedScroll(View child) {\n        info(\"onStopNestedScroll: mNestedScrollInProgress = \" + mNestedScrollInProgress);\n        mNestedScrollingParentHelper.onStopNestedScroll(child);\n        if (mNestedScrollInProgress) {\n            mNestedScrollInProgress = false;\n            mIsDragging = false;\n            if (!mNestScrollDurationRefreshing) {\n                finishPull(0);\n            }\n\n        }\n    }\n\n    @Override\n    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {\n        info(\"onNestedPreFling: mTargetCurrentOffset = \" + mTargetCurrentOffset +\n                \" ; velocityX = \" + velocityX + \" ; velocityY = \" + velocityY);\n        if (mTargetCurrentOffset > mTargetInitOffset) {\n            mNestedScrollInProgress = false;\n            mIsDragging = false;\n            if (!mNestScrollDurationRefreshing) {\n                finishPull((int) -velocityY);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {\n        try {\n            return super.onNestedFling(target, velocityX, velocityY, consumed);\n        } catch (Throwable e) {\n            // android 24及以上ViewGroup会继续往上派发， 23以及以下直接返回false\n            // 低于5.0的机器和RecyclerView配合工作时，部分机型会调用这个方法，但是ViewGroup并没有实现这个方法，会报错，这里catch一下\n        }\n        return false;\n    }\n\n    private int moveTargetView(float dy) {\n        int target = (int) (mTargetCurrentOffset + dy);\n        return moveTargetViewTo(target);\n    }\n\n    private int moveTargetViewTo(int target) {\n        return moveTargetViewTo(target, false);\n    }\n\n    private int moveTargetViewTo(int target, boolean calculateAnyWay) {\n        target = calculateTargetOffset(target, mTargetInitOffset, mTargetRefreshOffset, mEnableOverPull);\n        int offset = 0;\n        if (target != mTargetCurrentOffset || calculateAnyWay) {\n            offset = target - mTargetCurrentOffset;\n            ViewCompat.offsetTopAndBottom(mTargetView, offset);\n            mTargetCurrentOffset = target;\n            int total = mTargetRefreshOffset - mTargetInitOffset;\n            if (!mIsRefreshing) {\n                mIRefreshView.onPull(Math.min(mTargetCurrentOffset - mTargetInitOffset, total), total,\n                        mTargetCurrentOffset - mTargetRefreshOffset);\n            }\n            onMoveTargetView(mTargetCurrentOffset);\n            if (mListener != null) {\n                mListener.onMoveTarget(mTargetCurrentOffset);\n            }\n\n            if (mRefreshOffsetCalculator == null) {\n                mRefreshOffsetCalculator = new QMUIDefaultRefreshOffsetCalculator();\n            }\n            int newRefreshOffset = mRefreshOffsetCalculator.calculateRefreshOffset(mRefreshInitOffset, mRefreshEndOffset, mRefreshView.getMeasuredHeight(),\n                    mTargetCurrentOffset, mTargetInitOffset, mTargetRefreshOffset);\n            if (newRefreshOffset != mRefreshCurrentOffset) {\n                ViewCompat.offsetTopAndBottom(mRefreshView, newRefreshOffset - mRefreshCurrentOffset);\n                mRefreshCurrentOffset = newRefreshOffset;\n                onMoveRefreshView(mRefreshCurrentOffset);\n                if (mListener != null) {\n                    mListener.onMoveRefreshView(mRefreshCurrentOffset);\n                }\n            }\n        }\n        return offset;\n    }\n\n    protected int calculateTargetOffset(int target, int targetInitOffset, int targetRefreshOffset, boolean enableOverPull) {\n        target = Math.max(target, targetInitOffset);\n        if (!enableOverPull) {\n            target = Math.min(target, targetRefreshOffset);\n        }\n        return target;\n    }\n\n    private void acquireVelocityTracker(final MotionEvent event) {\n        if (null == mVelocityTracker) {\n            mVelocityTracker = VelocityTracker.obtain();\n        }\n        mVelocityTracker.addMovement(event);\n    }\n\n    private void releaseVelocityTracker() {\n        if (null != mVelocityTracker) {\n            mVelocityTracker.clear();\n            mVelocityTracker.recycle();\n            mVelocityTracker = null;\n        }\n    }\n\n    public int getRefreshInitOffset() {\n        return mRefreshInitOffset;\n    }\n\n    public int getRefreshEndOffset() {\n        return mRefreshEndOffset;\n    }\n\n    public int getTargetInitOffset() {\n        return mTargetInitOffset;\n    }\n\n    public int getTargetRefreshOffset() {\n        return mTargetRefreshOffset;\n    }\n\n    public void setTargetRefreshOffset(int targetRefreshOffset) {\n        mEqualTargetRefreshOffsetToRefreshViewHeight = false;\n        mTargetRefreshOffset = targetRefreshOffset;\n    }\n\n    public View getTargetView() {\n        return mTargetView;\n    }\n\n    protected void onMoveTargetView(int offset) {\n\n    }\n\n    protected void onMoveRefreshView(int offset) {\n\n    }\n\n\n    private boolean hasFlag(int flag) {\n        return (mScrollFlag & flag) == flag;\n    }\n\n    private void removeFlag(int flag) {\n        mScrollFlag = mScrollFlag & ~flag;\n    }\n\n    @Override\n    public void computeScroll() {\n        if (mScroller.computeScrollOffset()) {\n            int offsetY = mScroller.getCurrY();\n            moveTargetViewTo(offsetY);\n            if (offsetY <= 0 && hasFlag(FLAG_NEED_DELIVER_VELOCITY)) {\n                deliverVelocity();\n                mScroller.forceFinished(true);\n            }\n            invalidate();\n        } else if (hasFlag(FLAG_NEED_SCROLL_TO_INIT_POSITION)) {\n            removeFlag(FLAG_NEED_SCROLL_TO_INIT_POSITION);\n            if (mTargetCurrentOffset != mTargetInitOffset) {\n                mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetInitOffset - mTargetCurrentOffset);\n            }\n            invalidate();\n        } else if (hasFlag(FLAG_NEED_SCROLL_TO_REFRESH_POSITION)) {\n            removeFlag(FLAG_NEED_SCROLL_TO_REFRESH_POSITION);\n            if (mTargetCurrentOffset != mTargetRefreshOffset) {\n                mScroller.startScroll(0, mTargetCurrentOffset, 0, mTargetRefreshOffset - mTargetCurrentOffset);\n            } else {\n                moveTargetViewTo(mTargetRefreshOffset, true);\n            }\n            invalidate();\n        } else if (hasFlag(FLAG_NEED_DO_REFRESH)) {\n            removeFlag(FLAG_NEED_DO_REFRESH);\n            onRefresh();\n            moveTargetViewTo(mTargetRefreshOffset, true);\n        } else {\n            deliverVelocity();\n        }\n    }\n\n    private void deliverVelocity() {\n        if (hasFlag(FLAG_NEED_DELIVER_VELOCITY)) {\n            removeFlag(FLAG_NEED_DELIVER_VELOCITY);\n            if (mScroller.getCurrVelocity() > mMiniVelocity) {\n                info(\"deliver velocity: \" + mScroller.getCurrVelocity());\n                // if there is a velocity, pass it on\n                if (mTargetView instanceof RecyclerView) {\n                    ((RecyclerView) mTargetView).fling(0, (int) mScroller.getCurrVelocity());\n                } else if (mTargetView instanceof AbsListView && android.os.Build.VERSION.SDK_INT >= 21) {\n                    ((AbsListView) mTargetView).fling((int) mScroller.getCurrVelocity());\n                }\n            }\n        }\n    }\n\n    private void info(String msg) {\n        if (QMUIConfig.DEBUG) {\n            Log.i(TAG, msg);\n        }\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        final int action = ev.getAction();\n        if (action == MotionEvent.ACTION_DOWN) {\n            mNestScrollDurationRefreshing = mIsRefreshing || (mScrollFlag & FLAG_NEED_DO_REFRESH) != 0;\n        } else if (mNestScrollDurationRefreshing) {\n            if (action == MotionEvent.ACTION_MOVE) {\n                if (!mIsRefreshing && mScroller.isFinished() && mScrollFlag == 0) {\n                    // 这里必须要 dispatch 一次 down 事件，否则不能触发 NestScroll，具体可参考 RecyclerView\n                    // down 过程中会触发 onStopNestedScroll，mNestScrollDurationRefreshing 必须在之后\n                    // 置为false，否则会触发 finishPull\n                    ev.offsetLocation(0, -mSystemTouchSlop - 1);\n                    ev.setAction(MotionEvent.ACTION_DOWN);\n                    super.dispatchTouchEvent(ev);\n                    mNestScrollDurationRefreshing = false;\n                    ev.setAction(action);\n                    // offset touch slop, 避免触发点击事件\n                    ev.offsetLocation(0, mSystemTouchSlop + 1);\n                }\n            } else {\n                mNestScrollDurationRefreshing = false;\n            }\n        }\n\n        return super.dispatchTouchEvent(ev);\n    }\n\n    public interface OnPullListener {\n\n        void onMoveTarget(int offset);\n\n        void onMoveRefreshView(int offset);\n\n        void onRefresh();\n    }\n\n\n    public interface OnChildScrollUpCallback {\n        boolean canChildScrollUp(QMUIPullRefreshLayout parent, @Nullable View child);\n    }\n\n    public interface RefreshOffsetCalculator {\n\n        /**\n         * 通过 targetView 的当前位置、targetView 的初始和刷新位置以及 refreshView 的初始与结束位置计算 RefreshView 的位置。\n         *\n         * @param refreshInitOffset   RefreshView 的初始 offset。\n         * @param refreshEndOffset    刷新时 RefreshView 的 offset。\n         * @param refreshViewHeight   RerfreshView 的高度\n         * @param targetCurrentOffset 下拉时 TargetView（ListView 或者 ScrollView 等）当前的位置。\n         * @param targetInitOffset    TargetView（ListView 或者 ScrollView 等）的初始位置。\n         * @param targetRefreshOffset 刷新时 TargetView（ListView 或者 ScrollView等）的位置。\n         * @return RefreshView 当前的位置。\n         */\n        int calculateRefreshOffset(int refreshInitOffset, int refreshEndOffset, int refreshViewHeight,\n                                   int targetCurrentOffset, int targetInitOffset, int targetRefreshOffset);\n    }\n\n    public interface IRefreshView {\n        void stop();\n\n        void doRefresh();\n\n        void onPull(int offset, int total, int overPull);\n    }\n\n    public static class RefreshView extends AppCompatImageView implements IRefreshView, IQMUISkinDefaultAttrProvider {\n        private static final int MAX_ALPHA = 255;\n        private static final float TRIM_RATE = 0.85f;\n        private static final float TRIM_OFFSET = 0.4f;\n\n        static final int CIRCLE_DIAMETER = 40;\n        static final int CIRCLE_DIAMETER_LARGE = 56;\n\n        private CircularProgressDrawable mProgress;\n        private int mCircleDiameter;\n\n        private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n        static {\n            sDefaultSkinAttrs = new SimpleArrayMap<>(4);\n            sDefaultSkinAttrs.put(QMUISkinValueBuilder.TINT_COLOR, R.attr.qmui_skin_support_pull_refresh_view_color);\n        }\n\n        public RefreshView(Context context) {\n            super(context);\n            mProgress = new CircularProgressDrawable(context);\n            setColorSchemeColors(QMUIResHelper.getAttrColor(\n                    context, R.attr.qmui_skin_support_pull_refresh_view_color));\n            mProgress.setStyle(CircularProgressDrawable.LARGE);\n            mProgress.setAlpha(MAX_ALPHA);\n            mProgress.setArrowScale(0.8f);\n            setImageDrawable(mProgress);\n            final DisplayMetrics metrics = getResources().getDisplayMetrics();\n            mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            setMeasuredDimension(mCircleDiameter, mCircleDiameter);\n        }\n\n        @Override\n        public void onPull(int offset, int total, int overPull) {\n            if (mProgress.isRunning()) {\n                return;\n            }\n            float end = TRIM_RATE * offset / total;\n            float rotate = TRIM_OFFSET * offset / total;\n            if (overPull > 0) {\n                rotate += TRIM_OFFSET * overPull / total;\n            }\n            mProgress.setArrowEnabled(true);\n            mProgress.setStartEndTrim(0, end);\n            mProgress.setProgressRotation(rotate);\n        }\n\n        public void setSize(@CircularProgressDrawable.ProgressDrawableSize int size) {\n            if (size != CircularProgressDrawable.LARGE && size != CircularProgressDrawable.DEFAULT) {\n                return;\n            }\n            final DisplayMetrics metrics = getResources().getDisplayMetrics();\n            if (size == CircularProgressDrawable.LARGE) {\n                mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);\n            } else {\n                mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);\n            }\n            // force the bounds of the progress circle inside the circle view to\n            // update by setting it to null before updating its size and then\n            // re-setting it\n            setImageDrawable(null);\n            mProgress.setStyle(size);\n            setImageDrawable(mProgress);\n        }\n\n        public void stop() {\n            mProgress.stop();\n        }\n\n        public void doRefresh() {\n            mProgress.start();\n        }\n\n        public void setColorSchemeResources(@ColorRes int... colorResIds) {\n            final Context context = getContext();\n            int[] colorRes = new int[colorResIds.length];\n            for (int i = 0; i < colorResIds.length; i++) {\n                colorRes[i] = ContextCompat.getColor(context, colorResIds[i]);\n            }\n            setColorSchemeColors(colorRes);\n        }\n\n        public void setColorSchemeColors(@ColorInt int... colors) {\n            mProgress.setColorSchemeColors(colors);\n        }\n\n        @Override\n        public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n            return sDefaultSkinAttrs;\n        }\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/roundwidget/QMUIRoundButton.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.roundwidget;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaButton;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\n/**\n * 使按钮能方便地指定圆角、边框颜色、边框粗细、背景色\n * <p>\n * 注意: 因为该控件的圆角采用 View 的 background 实现, 所以与原生的 <code>android:background</code> 有冲突。\n * <ul>\n * <li>如果在 xml 中用 <code>android:background</code> 指定 background, 该 background 不会生效。</li>\n * <li>如果在该 View 构造完后用 {@link #setBackgroundResource(int)} 等方法设置背景, 该背景将覆盖圆角效果。</li>\n * </ul>\n * </p>\n * <p>\n * 如需在 xml 中指定圆角、边框颜色、边框粗细、背景色等值,采用 xml 属性 {@link com.qmuiteam.qmui.R.styleable#QMUIRoundButton}\n * </p>\n * <p>\n * 如需在 Java 中指定以上属性, 需要通过 {@link #getBackground()} 获取 {@link QMUIRoundButtonDrawable} 对象,\n * 然后使用 {@link QMUIRoundButtonDrawable} 提供的方法进行设置。\n * </p>\n * <p>\n *\n * @see QMUIRoundButtonDrawable\n * </p>\n */\npublic class QMUIRoundButton extends QMUIAlphaButton implements IQMUISkinDefaultAttrProvider {\n\n    private QMUIRoundButtonDrawable mRoundBg;\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(3);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_round_btn_bg_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BORDER, R.attr.qmui_skin_support_round_btn_border_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.TEXT_COLOR, R.attr.qmui_skin_support_round_btn_text_color);\n    }\n\n\n    public QMUIRoundButton(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRoundButton(Context context, AttributeSet attrs) {\n        super(context, attrs, R.attr.QMUIButtonStyle);\n        init(context, attrs, R.attr.QMUIButtonStyle);\n    }\n\n    public QMUIRoundButton(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        mRoundBg = QMUIRoundButtonDrawable.fromAttributeSet(context, attrs, defStyleAttr);\n        QMUIViewHelper.setBackgroundKeepingPadding(this, mRoundBg);\n        setChangeAlphaWhenDisable(false);\n        setChangeAlphaWhenPress(false);\n    }\n\n    @Override\n    public void setBackgroundColor(int color) {\n        mRoundBg.setBgData(ColorStateList.valueOf(color));\n    }\n\n    public void setBgData(@Nullable ColorStateList colors) {\n        mRoundBg.setBgData(colors);\n    }\n\n    public void setStrokeData(int width, @Nullable ColorStateList colors) {\n        mRoundBg.setStrokeData(width, colors);\n    }\n\n    public int getStrokeWidth(){\n        return mRoundBg.getStrokeWidth();\n    }\n\n    public void setStrokeColors(ColorStateList colors) {\n        mRoundBg.setStrokeColors(colors);\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/roundwidget/QMUIRoundButtonDrawable.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.roundwidget;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Rect;\nimport android.graphics.drawable.GradientDrawable;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\n\n/**\n * 可以方便地生成圆角矩形/圆形 {@link android.graphics.drawable.Drawable}。\n * <p>\n * <ul>\n * <li>使用 {@link #setBgData(ColorStateList)} 设置背景色。</li>\n * <li>使用 {@link #setStrokeData(int, ColorStateList)} 设置描边大小、描边颜色。</li>\n * <li>使用 {@link #setIsRadiusAdjustBounds(boolean)} 设置圆角大小是否自动适应为 {@link android.view.View} 的高度的一半, 默认为 true。</li>\n * </ul>\n */\npublic class QMUIRoundButtonDrawable extends GradientDrawable {\n\n    /**\n     * 圆角大小是否自适应为 View 的高度的一般\n     */\n    private boolean mRadiusAdjustBounds = true;\n    private ColorStateList mFillColors;\n    private int mStrokeWidth = 0;\n    private ColorStateList mStrokeColors;\n\n    /**\n     * 设置按钮的背景色(只支持纯色,不支持 Bitmap 或 Drawable)\n     */\n    public void setBgData(@Nullable ColorStateList colors) {\n        super.setColor(colors);\n    }\n\n    /**\n     * 设置按钮的描边粗细和颜色\n     */\n    public void setStrokeData(int width, @Nullable ColorStateList colors) {\n        mStrokeWidth = width;\n        mStrokeColors = colors;\n        super.setStroke(width, colors);\n    }\n\n    public int getStrokeWidth(){\n        return mStrokeWidth;\n    }\n\n    public void setStrokeColors(@Nullable ColorStateList colors){\n        setStrokeData(mStrokeWidth, colors);\n    }\n\n    /**\n     * 设置圆角大小是否自动适应为 View 的高度的一半\n     */\n    public void setIsRadiusAdjustBounds(boolean isRadiusAdjustBounds) {\n        mRadiusAdjustBounds = isRadiusAdjustBounds;\n    }\n\n    @Override\n    protected boolean onStateChange(int[] stateSet) {\n        boolean superRet = super.onStateChange(stateSet);\n        if (mFillColors != null) {\n            int color = mFillColors.getColorForState(stateSet, 0);\n            setColor(color);\n            superRet = true;\n        }\n        if (mStrokeColors != null) {\n            int color = mStrokeColors.getColorForState(stateSet, 0);\n            setStroke(mStrokeWidth, color);\n            superRet = true;\n        }\n        return superRet;\n    }\n\n    @Override\n    public boolean isStateful() {\n        return (mFillColors != null && mFillColors.isStateful())\n                || (mStrokeColors != null && mStrokeColors.isStateful())\n                || super.isStateful();\n    }\n\n    @Override\n    protected void onBoundsChange(Rect r) {\n        super.onBoundsChange(r);\n        if (mRadiusAdjustBounds) {\n            // 修改圆角为短边的一半\n            setCornerRadius(Math.min(r.width(), r.height()) / 2);\n        }\n    }\n\n    public static QMUIRoundButtonDrawable fromAttributeSet(Context context, AttributeSet attrs, int defStyleAttr) {\n        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.QMUIRoundButton, defStyleAttr, 0);\n        ColorStateList colorBg = typedArray.getColorStateList(R.styleable.QMUIRoundButton_qmui_backgroundColor);\n        ColorStateList colorBorder = typedArray.getColorStateList(R.styleable.QMUIRoundButton_qmui_borderColor);\n        int borderWidth = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_borderWidth, 0);\n        boolean isRadiusAdjustBounds = typedArray.getBoolean(R.styleable.QMUIRoundButton_qmui_isRadiusAdjustBounds, false);\n        int mRadius = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_radius, 0);\n        int mRadiusTopLeft = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_radiusTopLeft, 0);\n        int mRadiusTopRight = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_radiusTopRight, 0);\n        int mRadiusBottomLeft = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_radiusBottomLeft, 0);\n        int mRadiusBottomRight = typedArray.getDimensionPixelSize(R.styleable.QMUIRoundButton_qmui_radiusBottomRight, 0);\n        typedArray.recycle();\n\n        QMUIRoundButtonDrawable bg = new QMUIRoundButtonDrawable();\n        bg.setBgData(colorBg);\n        bg.setStrokeData(borderWidth, colorBorder);\n        if (mRadiusTopLeft > 0 || mRadiusTopRight > 0 || mRadiusBottomLeft > 0 || mRadiusBottomRight > 0) {\n            float[] radii = new float[]{\n                    mRadiusTopLeft, mRadiusTopLeft,\n                    mRadiusTopRight, mRadiusTopRight,\n                    mRadiusBottomRight, mRadiusBottomRight,\n                    mRadiusBottomLeft, mRadiusBottomLeft\n            };\n            bg.setCornerRadii(radii);\n            isRadiusAdjustBounds = false;\n        } else {\n            bg.setCornerRadius(mRadius);\n            if(mRadius > 0){\n                isRadiusAdjustBounds = false;\n            }\n        }\n        bg.setIsRadiusAdjustBounds(isRadiusAdjustBounds);\n        return bg;\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/roundwidget/QMUIRoundFrameLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.roundwidget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\n/**\n * 见 {@link QMUIRoundButton} 与 {@link QMUIRoundButtonDrawable}\n */\npublic class QMUIRoundFrameLayout extends FrameLayout {\n\n    public QMUIRoundFrameLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRoundFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n    }\n\n    public QMUIRoundFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        QMUIRoundButtonDrawable bg = QMUIRoundButtonDrawable.fromAttributeSet(context, attrs, defStyleAttr);\n        QMUIViewHelper.setBackgroundKeepingPadding(this, bg);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/roundwidget/QMUIRoundLinearLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.roundwidget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\n/**\n * 见 {@link QMUIRoundButton} 与 {@link QMUIRoundButtonDrawable}\n */\npublic class QMUIRoundLinearLayout extends LinearLayout {\n\n    public QMUIRoundLinearLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRoundLinearLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, R.attr.QMUIButtonStyle);\n    }\n\n    public QMUIRoundLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        QMUIRoundButtonDrawable bg = QMUIRoundButtonDrawable.fromAttributeSet(context, attrs, 0);\n        QMUIViewHelper.setBackgroundKeepingPadding(this, bg);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/roundwidget/QMUIRoundRelativeLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.roundwidget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.RelativeLayout;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\n\n/**\n * 见 {@link QMUIRoundButton} 与 {@link QMUIRoundButtonDrawable}\n */\npublic class QMUIRoundRelativeLayout extends RelativeLayout {\n\n    public QMUIRoundRelativeLayout(Context context) {\n        super(context);\n        init(context, null, 0);\n    }\n\n    public QMUIRoundRelativeLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, R.attr.QMUIButtonStyle);\n    }\n\n    public QMUIRoundRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        QMUIRoundButtonDrawable bg = QMUIRoundButtonDrawable.fromAttributeSet(context, attrs, defStyleAttr);\n        QMUIViewHelper.setBackgroundKeepingPadding(this, bg);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUIDefaultStickySectionAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport androidx.annotation.NonNull;\nimport android.view.View;\nimport android.view.ViewGroup;\n\npublic abstract class QMUIDefaultStickySectionAdapter<\n        H extends QMUISection.Model<H>,\n        T extends QMUISection.Model<T>> extends\n        QMUIStickySectionAdapter<H, T, QMUIStickySectionAdapter.ViewHolder> {\n\n    public QMUIDefaultStickySectionAdapter() {\n    }\n\n    public QMUIDefaultStickySectionAdapter(boolean removeSectionTitleIfOnlyOneSection) {\n        super(removeSectionTitleIfOnlyOneSection);\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateSectionLoadingViewHolder(@NonNull ViewGroup viewGroup) {\n        return new ViewHolder(new View(viewGroup.getContext()));\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateCustomItemViewHolder(@NonNull ViewGroup viewGroup, int type) {\n        return new ViewHolder(new View(viewGroup.getContext()));\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUISection.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUISection<H extends QMUISection.Model<H>, T extends QMUISection.Model<T>> {\n    public static final int SECTION_INDEX_UNKNOWN = -1;\n    public static final int ITEM_INDEX_UNKNOWN = -1;\n    public static final int ITEM_INDEX_SECTION_HEADER = -2;\n    public static final int ITEM_INDEX_LOAD_BEFORE = -3;\n    public static final int ITEM_INDEX_LOAD_AFTER = -4;\n    /**\n     * if add internal index, we should update this item\n     */\n    public static final int ITEM_INDEX_INTERNAL_END = -4;\n    /**\n     * offset custom index to reduce conflict with internal index\n     */\n    public static final int ITEM_INDEX_CUSTOM_OFFSET = -1000;\n\n    private H mHeader;\n    private ArrayList<T> mItemList;\n    private boolean mIsFold;\n    private boolean mIsLocked;\n    private boolean mExistBeforeDataToLoad;\n    private boolean mExistAfterDataToLoad;\n    private boolean mIsErrorToLoadBefore = false;\n    private boolean mIsErrorToLoadAfter = false;\n\n\n    public QMUISection(@NonNull H header, @Nullable List<T> itemList) {\n        this(header, itemList, false);\n    }\n\n    public QMUISection(@NonNull H header, @Nullable List<T> itemList, boolean isFold) {\n        this(header, itemList, isFold, false, false, false);\n    }\n\n\n    public QMUISection(@NonNull H header, @Nullable List<T> itemList, boolean isFold,\n                       boolean isLocked, boolean existBeforeDataToLoad, boolean existAfterDataToLoad) {\n        mHeader = header;\n        mItemList = new ArrayList<>();\n        if (itemList != null) {\n            mItemList.addAll(itemList);\n        }\n        mIsFold = isFold;\n        mIsLocked = isLocked;\n        mExistBeforeDataToLoad = existBeforeDataToLoad;\n        mExistAfterDataToLoad = existAfterDataToLoad;\n    }\n\n    public H getHeader() {\n        return mHeader;\n    }\n\n    public boolean isFold() {\n        return mIsFold;\n    }\n\n    public void setFold(boolean fold) {\n        mIsFold = fold;\n    }\n\n    public boolean isLocked() {\n        return mIsLocked;\n    }\n\n    public void setLocked(boolean locked) {\n        mIsLocked = locked;\n    }\n\n    public boolean isExistBeforeDataToLoad() {\n        return mExistBeforeDataToLoad;\n    }\n\n    public void setExistBeforeDataToLoad(boolean existBeforeDataToLoad) {\n        mExistBeforeDataToLoad = existBeforeDataToLoad;\n    }\n\n    public boolean isExistAfterDataToLoad() {\n        return mExistAfterDataToLoad;\n    }\n\n    public void setExistAfterDataToLoad(boolean existAfterDataToLoad) {\n        mExistAfterDataToLoad = existAfterDataToLoad;\n    }\n\n    public boolean isErrorToLoadBefore() {\n        return mIsErrorToLoadBefore;\n    }\n\n    public void setErrorToLoadBefore(boolean errorToLoadBefore) {\n        mIsErrorToLoadBefore = errorToLoadBefore;\n    }\n\n    public boolean isErrorToLoadAfter() {\n        return mIsErrorToLoadAfter;\n    }\n\n    public void setErrorToLoadAfter(boolean errorToLoadAfter) {\n        mIsErrorToLoadAfter = errorToLoadAfter;\n    }\n\n    public int getItemCount() {\n        return mItemList.size();\n    }\n\n    public T getItemAt(int index){\n        if (index < 0 || index >= mItemList.size()) {\n            return null;\n        }\n        return mItemList.get(index);\n    }\n\n    public boolean existItem(T item){\n        return mItemList.contains(item);\n    }\n\n    public void finishLoadMore(@Nullable List<T> data, boolean isLoadBefore, boolean existMoreData){\n        if(isLoadBefore){\n            if(data != null){\n                mItemList.addAll(0, data);\n            }\n            mExistBeforeDataToLoad = existMoreData;\n\n        }else{\n            if(data != null){\n                mItemList.addAll(data);\n            }\n            mExistAfterDataToLoad = existMoreData;\n        }\n    }\n\n    public void cloneStatusTo(QMUISection<H, T> other) {\n        other.mExistBeforeDataToLoad = mExistBeforeDataToLoad;\n        other.mExistAfterDataToLoad = mExistAfterDataToLoad;\n        other.mIsFold = mIsFold;\n        other.mIsLocked = mIsLocked;\n        other.mIsErrorToLoadBefore = mIsErrorToLoadBefore;\n        other.mIsErrorToLoadAfter = mIsErrorToLoadAfter;\n    }\n\n    public QMUISection<H, T> mutate(){\n        QMUISection<H, T> section = new QMUISection<>(mHeader, mItemList,\n                mIsFold, mIsLocked, mExistBeforeDataToLoad, mExistAfterDataToLoad);\n        section.mIsErrorToLoadBefore = mIsErrorToLoadBefore;\n        section.mIsErrorToLoadAfter = mIsErrorToLoadAfter;\n        return section;\n    }\n\n    public QMUISection<H, T> cloneForDiff() {\n        ArrayList<T> newList = new ArrayList<>();\n        for (T item : mItemList) {\n            newList.add(item.cloneForDiff());\n        }\n        QMUISection<H, T> section = new QMUISection<>(mHeader.cloneForDiff(), newList,\n                mIsFold, mIsLocked, mExistBeforeDataToLoad, mExistAfterDataToLoad);\n        section.mIsErrorToLoadBefore = mIsErrorToLoadBefore;\n        section.mIsErrorToLoadAfter = mIsErrorToLoadAfter;\n        return section;\n    }\n\n    public static final boolean isCustomItemIndex(int index){\n        return index < ITEM_INDEX_INTERNAL_END;\n    }\n\n    public interface Model<T> {\n        /**\n         * Called by QMUISection to clone this model for next diff if the adapter data is mutable.\n         * you just need clone the fields needed for diff\n         *\n         * @return another instance of T\n         */\n        T cloneForDiff();\n\n        /**\n         * Called by QMUIDiffCallback decide whether two object represent the same T.\n         * For example, if your items have unique ids, this method should check their id equality.\n         *\n         * @param other the object to compare\n         * @return True if the two items represent the same object or false if they are different.\n         */\n        boolean isSameItem(T other);\n\n        /**\n         * Called by the QMUIDiffCallback when it wants to check whether two items have the same data.\n         * QMUIDiffCallback uses this information to detect if the contents of an item has changed.\n         *\n         * @param other the object to compare\n         * @return True if the contents of the items are the same or false if they are different.\n         */\n        boolean isSameContent(T other);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUISectionDiffCallback.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.DiffUtil;\nimport android.util.SparseIntArray;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_LOAD_AFTER;\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_LOAD_BEFORE;\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_SECTION_HEADER;\n\npublic class QMUISectionDiffCallback<H extends QMUISection.Model<H>, T extends QMUISection.Model<T>>\n        extends DiffUtil.Callback {\n\n    private ArrayList<QMUISection<H, T>> mOldList = new ArrayList<>();\n    private ArrayList<QMUISection<H, T>> mNewList = new ArrayList<>();\n\n    private ArrayList<Integer> mOldSectionIndex = new ArrayList<>();\n    private ArrayList<Integer> mOldItemIndex = new ArrayList<>();\n\n    private ArrayList<Integer> mNewSectionIndex = new ArrayList<>();\n    private ArrayList<Integer> mNewItemIndex = new ArrayList<>();\n    private boolean mRemoveSectionTitleIfOnlyOnceSection;\n\n    public QMUISectionDiffCallback(\n            @Nullable List<QMUISection<H, T>> oldList,\n            @Nullable List<QMUISection<H, T>> newList) {\n        if (oldList != null) {\n            mOldList.addAll(oldList);\n        }\n\n        if (newList != null) {\n            mNewList.addAll(newList);\n        }\n    }\n\n    void generateIndex(boolean removeSectionTitleIfOnlyOnceSection){\n        mRemoveSectionTitleIfOnlyOnceSection = removeSectionTitleIfOnlyOnceSection;\n        generateIndex(mOldList, mOldSectionIndex, mOldItemIndex, removeSectionTitleIfOnlyOnceSection);\n        generateIndex(mNewList, mNewSectionIndex, mNewItemIndex, removeSectionTitleIfOnlyOnceSection);\n    }\n\n    public void cloneNewIndexTo(@NonNull ArrayList<Integer> sectionIndex, @NonNull ArrayList<Integer> itemIndex) {\n        sectionIndex.clear();\n        itemIndex.clear();\n        sectionIndex.ensureCapacity(mNewSectionIndex.size());\n        itemIndex.ensureCapacity(mNewItemIndex.size());\n        for (int i = 0; i < mNewSectionIndex.size(); i++) {\n            sectionIndex.add(i, mNewSectionIndex.get(i));\n        }\n        for (int i = 0; i < mNewItemIndex.size(); i++) {\n            itemIndex.add(i, mNewItemIndex.get(i));\n        }\n    }\n\n    private void generateIndex(List<QMUISection<H, T>> list,\n                               ArrayList<Integer> sectionIndex, ArrayList<Integer> itemIndex,\n                               boolean removeSectionTitleIfOnlyOnceSection) {\n        sectionIndex.clear();\n        itemIndex.clear();\n        IndexGenerationInfo generationInfo = new IndexGenerationInfo(sectionIndex, itemIndex);\n        if (list.isEmpty() || !list.get(0).isLocked()) {\n            onGenerateCustomIndexBeforeSectionList(generationInfo, list);\n        }\n\n        for (int i = 0; i < list.size(); i++) {\n            QMUISection<H, T> section = list.get(i);\n            if (section.isLocked()) {\n                continue;\n            }\n            if(!removeSectionTitleIfOnlyOnceSection || list.size() > 1){\n                generationInfo.appendIndex(i, ITEM_INDEX_SECTION_HEADER);\n            }\n            if (section.isFold()) {\n                continue;\n            }\n            onGenerateCustomIndexBeforeItemList(generationInfo, section, i);\n            if (section.isExistBeforeDataToLoad()) {\n                generationInfo.appendIndex(i, ITEM_INDEX_LOAD_BEFORE);\n            }\n\n            for (int j = 0; j < section.getItemCount(); j++) {\n                generationInfo.appendIndex(i, j);\n            }\n\n            if (section.isExistAfterDataToLoad()) {\n                generationInfo.appendIndex(i, ITEM_INDEX_LOAD_AFTER);\n            }\n            onGenerateCustomIndexAfterItemList(generationInfo, section, i);\n        }\n        if (list.isEmpty()) {\n            onGenerateCustomIndexAfterSectionList(generationInfo, list);\n        } else {\n            QMUISection lastSection = list.get(list.size() - 1);\n            if (!lastSection.isLocked() && (lastSection.isFold() || !lastSection.isExistAfterDataToLoad())) {\n                onGenerateCustomIndexAfterSectionList(generationInfo, list);\n            }\n        }\n    }\n\n    /**\n     * Subclasses overrides this method to add custom view before the beginning of the list, such as list header.\n     * Use {@link IndexGenerationInfo#appendWholeListCustomIndex(int)} to add index info\n     *\n     * @param generationInfo call generationInfo.appendWholeListCustomIndex to collect index info\n     * @param list           the whole list info\n     */\n    protected void onGenerateCustomIndexBeforeSectionList(IndexGenerationInfo generationInfo, List<QMUISection<H, T>> list) {\n\n    }\n\n    /**\n     * Subclasses overrides this method to add custom view after the end of the list, such as list footer.\n     * Use {@link IndexGenerationInfo#appendWholeListCustomIndex(int)} to add index info\n     *\n     * @param generationInfo call generationInfo.appendWholeListCustomIndex to collect index info\n     * @param list           the whole list info\n     */\n    protected void onGenerateCustomIndexAfterSectionList(IndexGenerationInfo generationInfo, List<QMUISection<H, T>> list) {\n\n    }\n\n    /**\n     * Subclasses overrides this method to add custom view before the beginning of the section content list\n     * Use {@link IndexGenerationInfo#appendCustomIndex(int, int)} to add index info\n     *\n     * @param generationInfo call generationInfo.appendIndex to collect index info\n     * @param section        section info\n     * @param sectionIndex   section index info\n     */\n    protected void onGenerateCustomIndexBeforeItemList(IndexGenerationInfo generationInfo, QMUISection<H, T> section, int sectionIndex) {\n\n    }\n\n    /**\n     * Subclasses overrides this method to add custom view before the end of the section content list\n     * Use {@link IndexGenerationInfo#appendIndex(int, int)} to add index info\n     *\n     * @param generationInfo call generationInfo.appendCustomIndex to collect index info\n     * @param section        section info\n     * @param sectionIndex   section index info\n     */\n    protected void onGenerateCustomIndexAfterItemList(IndexGenerationInfo generationInfo, QMUISection<H, T> section, int sectionIndex) {\n\n    }\n\n    /**\n     * Subclasses overrides this method to check whether two custom items have the same data\n     * @param oldSection the old section in the old list\n     * @param oldItemIndex the old item index in old section\n     * @param newSection the new section in the new list\n     * @param newItemIndex the new item index in new section\n     * @return True if the contents of the items are the same or false if they are different.\n     */\n    protected boolean areCustomContentsTheSame(@Nullable QMUISection<H, T> oldSection, int oldItemIndex,\n                                                   @Nullable QMUISection<H, T> newSection, int newItemIndex) {\n        return false;\n    }\n\n    @Override\n    public int getOldListSize() {\n        return mOldSectionIndex.size();\n    }\n\n    @Override\n    public int getNewListSize() {\n        return mNewSectionIndex.size();\n    }\n\n    @Override\n    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {\n        int oldSectionIndex = mOldSectionIndex.get(oldItemPosition);\n        int oldItemIndex = mOldItemIndex.get(oldItemPosition);\n\n        int newSectionIndex = mNewSectionIndex.get(newItemPosition);\n        int newItemIndex = mNewItemIndex.get(newItemPosition);\n\n        if (oldSectionIndex < 0 || newSectionIndex < 0) {\n            return oldSectionIndex == newSectionIndex && oldItemIndex == newItemIndex;\n        }\n\n\n        QMUISection<H, T> oldModel = mOldList.get(oldSectionIndex);\n        QMUISection<H, T> newModel = mNewList.get(newSectionIndex);\n\n        if (!oldModel.getHeader().isSameItem(newModel.getHeader())) {\n            return false;\n        }\n\n\n        if (oldItemIndex < 0 && oldItemIndex == newItemIndex) {\n            return true;\n        }\n\n        if (oldItemIndex < 0 || newItemIndex < 0) {\n            return false;\n        }\n\n        T oldItem = oldModel.getItemAt(oldItemIndex);\n        T newItem = newModel.getItemAt(newItemIndex);\n\n        return (oldItem == null && newItem == null) ||\n                (oldItem != null && newItem != null && oldItem.isSameItem(newItem));\n    }\n\n    @Override\n    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {\n        int oldSectionIndex = mOldSectionIndex.get(oldItemPosition);\n        int oldItemIndex = mOldItemIndex.get(oldItemPosition);\n\n        int newSectionIndex = mNewSectionIndex.get(newItemPosition);\n        int newItemIndex = mNewItemIndex.get(newItemPosition);\n\n        if (newSectionIndex < 0) {\n            return areCustomContentsTheSame(null, oldItemIndex, null, newItemIndex);\n        }\n\n        if(mRemoveSectionTitleIfOnlyOnceSection){\n            // may be the indentation is changed.\n            if(mOldList.size() == 1 && mNewList.size() != 1){\n                return false;\n            }\n            if(mOldList.size() != 1 && mNewList.size() == 1){\n                return false;\n            }\n        }\n\n        QMUISection<H, T> oldModel = mOldList.get(oldSectionIndex);\n        QMUISection<H, T> newModel = mNewList.get(newSectionIndex);\n\n        if (oldItemIndex == ITEM_INDEX_SECTION_HEADER) {\n            return oldModel.isFold() == newModel.isFold() &&\n                    oldModel.getHeader().isSameContent(newModel.getHeader());\n        }\n\n        if (oldItemIndex == ITEM_INDEX_LOAD_BEFORE || oldItemIndex == ITEM_INDEX_LOAD_AFTER) {\n            // forced to return false，so we can trigger to load more\n            // in QMUIStickySectionAdapter.onViewAttachedToWindow\n            return false;\n        }\n\n        if (QMUISection.isCustomItemIndex(oldItemIndex)) {\n            return areCustomContentsTheSame(oldModel, oldItemIndex, newModel, newItemIndex);\n        }\n\n        T oldItem = oldModel.getItemAt(oldItemIndex);\n        T newItem = newModel.getItemAt(newItemIndex);\n\n        return (oldItem == null && newItem == null) ||\n                (oldItem != null && newItem != null && oldItem.isSameContent(newItem));\n    }\n\n    public static class IndexGenerationInfo {\n        private ArrayList<Integer> sectionIndexArray;\n        private ArrayList<Integer> itemIndexArray;\n\n        private IndexGenerationInfo(ArrayList<Integer> sectionIndex, ArrayList<Integer> itemIndex) {\n            sectionIndexArray = sectionIndex;\n            itemIndexArray = itemIndex;\n        }\n\n        public final void appendCustomIndex(int sectionIndex, int itemIndex) {\n\n            int offset = QMUISection.ITEM_INDEX_CUSTOM_OFFSET + itemIndex;\n            if(!QMUISection.isCustomItemIndex(offset)){\n                throw new IllegalArgumentException(\n                        \"Index conflicts with index used internally, please use negative number for custom item\");\n            }\n            appendIndex(sectionIndex, offset);\n        }\n\n        private void appendIndex(int sectionIndex, int itemIndex) {\n            if (sectionIndex < 0) {\n                throw new IllegalArgumentException(\"use appendWholeListCustomIndex for whole list\");\n            }\n            sectionIndexArray.add(sectionIndex);\n            itemIndexArray.add(itemIndex);\n        }\n\n        public final void appendWholeListCustomIndex(int itemIndex) {\n            int offset = QMUISection.ITEM_INDEX_CUSTOM_OFFSET + itemIndex;\n            if(!QMUISection.isCustomItemIndex(offset)){\n                throw new IllegalArgumentException(\n                        \"Index conflicts with index used internally, please use negative number for custom item\");\n            }\n            appendWholeListIndex(offset);\n        }\n\n        private void appendWholeListIndex(int itemIndex) {\n            sectionIndexArray.add(QMUISection.SECTION_INDEX_UNKNOWN);\n            itemIndexArray.add(itemIndex);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUIStickySectionAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.util.Log;\nimport android.util.SparseIntArray;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_CUSTOM_OFFSET;\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_LOAD_AFTER;\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_LOAD_BEFORE;\nimport static com.qmuiteam.qmui.widget.section.QMUISection.ITEM_INDEX_SECTION_HEADER;\n\npublic abstract class QMUIStickySectionAdapter<\n        H extends QMUISection.Model<H>, T extends QMUISection.Model<T>, VH extends QMUIStickySectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {\n    private static final String TAG = \"StickySectionAdapter\";\n    public static final int ITEM_TYPE_UNKNOWN = -1;\n    public static final int ITEM_TYPE_SECTION_HEADER = 0;\n    public static final int ITEM_TYPE_SECTION_ITEM = 1;\n    public static final int ITEM_TYPE_SECTION_LOADING = 2;\n    public static final int ITEM_TYPE_CUSTOM_OFFSET = 1000;\n\n    private List<QMUISection<H, T>> mBackupData = new ArrayList<>();\n    private List<QMUISection<H, T>> mCurrentData = new ArrayList<>();\n\n    private ArrayList<Integer> mSectionIndex = new ArrayList<>();\n    private ArrayList<Integer> mItemIndex = new ArrayList<>();\n    private ArrayList<QMUISection<H, T>> mLoadingBeforeSections = new ArrayList<>(2);\n    private ArrayList<QMUISection<H, T>> mLoadingAfterSections = new ArrayList<>(2);\n\n    private Callback<H, T> mCallback;\n    private ViewCallback mViewCallback;\n    private final boolean mRemoveSectionTitleIfOnlyOneSection;\n\n\n    public QMUIStickySectionAdapter() {\n        this(false);\n    }\n\n    public QMUIStickySectionAdapter(boolean removeSectionTitleIfOnlyOneSection) {\n        mRemoveSectionTitleIfOnlyOneSection = removeSectionTitleIfOnlyOneSection;\n    }\n\n    /**\n     * see {@link #setData(List, boolean, boolean)}\n     *\n     * @param data section list\n     */\n    public final void setData(@Nullable List<QMUISection<H, T>> data) {\n        setData(data, true);\n    }\n\n    /**\n     * see {@link #setData(List, boolean, boolean)}\n     *\n     * @param data section list\n     * @param onlyMutateState This is used to backup for next diff. True to use shallow copy, false tp use deep copy.\n     */\n    public final void setData(@Nullable List<QMUISection<H, T>> data, boolean onlyMutateState){\n        setData(data, onlyMutateState, true);\n    }\n\n    /**\n     * set the new data to the adapter, this will trigger diff between new data and old data.\n     * you should pay attention to the state of your data in memory. if new data and old data\n     * reference to the same data in memory, the diff will fail. This is why the parameter\n     * onlyMutateState exists:\n     * if onlyMutateState == true, shallow copy is used to backup for next diff. You must sure H, T in memory is\n     * different between old data and new data\n     * if onlyMutateState == false, deep copy is used to backup for next diff. It's safe, but it will consume\n     * unnecessary performance if your new data is different in memory.\n     *\n     * @param data            section list\n     * @param onlyMutateState This is used to backup for next diff. True to use shallow copy, false tp use deep copy.\n     * @param checkLock       check section lock\n     */\n    public final void setData(@Nullable List<QMUISection<H, T>> data, boolean onlyMutateState, boolean checkLock) {\n        mLoadingBeforeSections.clear();\n        mLoadingAfterSections.clear();\n        mCurrentData.clear();\n        if (data != null) {\n            mCurrentData.addAll(data);\n        }\n        beforeDiffInSet(mBackupData, mCurrentData);\n        if(!mCurrentData.isEmpty() && checkLock){\n            lock(mCurrentData.get(0));\n        }\n        diff(true, onlyMutateState);\n    }\n\n    /**\n     * Subclasses override this method to fill some info to new section list if need.\n     * For example, assume the user expand some section by click event, these action while\n     * modify old section list, but the new section list knows nothing for user action.\n     * so this method is a chance to synchronize some info from old section list.\n     *\n     * @param oldData old section list\n     * @param newData new section list\n     */\n    protected void beforeDiffInSet(List<QMUISection<H, T>> oldData, List<QMUISection<H, T>> newData) {\n\n    }\n\n    /**\n     *\n     * @param data              section list\n     * @param onlyMutateState   this is used to backup for next diff. True to use shallow copy, false tp use deep copy.\n     */\n    public final void setDataWithoutDiff(@Nullable List<QMUISection<H, T>> data, boolean onlyMutateState){\n        setDataWithoutDiff(data, onlyMutateState, true);\n    }\n\n    /**\n     * same as {@link #setData(List, boolean)}, but do't use {@link DiffUtil},\n     * use {@link #notifyDataSetChanged()} directly.\n     *\n     * @param data            section list\n     * @param onlyMutateState this is used to backup for next diff. True to use shallow copy, false tp use deep copy.\n     * @param checkLock       check section lock\n     */\n    public final void setDataWithoutDiff(@Nullable List<QMUISection<H, T>> data, boolean onlyMutateState, boolean checkLock) {\n        mLoadingBeforeSections.clear();\n        mLoadingAfterSections.clear();\n        mCurrentData.clear();\n        if (data != null) {\n            mCurrentData.addAll(data);\n        }\n        if(checkLock && !mCurrentData.isEmpty()){\n            lock(mCurrentData.get(0));\n        }\n        // only used to generate index info\n        QMUISectionDiffCallback callback = createDiffCallback(mBackupData, mCurrentData);\n        callback.generateIndex(mRemoveSectionTitleIfOnlyOneSection);\n        callback.cloneNewIndexTo(mSectionIndex, mItemIndex);\n        notifyDataSetChanged();\n        mBackupData.clear();\n        for (QMUISection<H, T> section : mCurrentData) {\n            mBackupData.add(onlyMutateState ? section.mutate() : section.cloneForDiff());\n        }\n    }\n\n    private void diff(boolean newDataSet, boolean onlyMutateState) {\n        QMUISectionDiffCallback callback = createDiffCallback(mBackupData, mCurrentData);\n        callback.generateIndex(mRemoveSectionTitleIfOnlyOneSection);\n        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback, false);\n        callback.cloneNewIndexTo(mSectionIndex, mItemIndex);\n        diffResult.dispatchUpdatesTo(this);\n\n        if (newDataSet || mBackupData.size() != mCurrentData.size()) {\n            mBackupData.clear();\n            for (QMUISection<H, T> section : mCurrentData) {\n                mBackupData.add(onlyMutateState ? section.mutate() : section.cloneForDiff());\n            }\n        } else {\n            //only status change, so we only copy statuses to mBackupData\n            for (int i = 0; i < mCurrentData.size(); i++) {\n                mCurrentData.get(i).cloneStatusTo(mBackupData.get(i));\n            }\n        }\n    }\n\n\n    /**\n     * section data is not changed, only custom item index may changed, so we also need to regenerate index\n     */\n    public void refreshCustomData() {\n        QMUISectionDiffCallback callback = createDiffCallback(mBackupData, mCurrentData);\n        callback.generateIndex(mRemoveSectionTitleIfOnlyOneSection);\n        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback, false);\n        callback.cloneNewIndexTo(mSectionIndex, mItemIndex);\n        diffResult.dispatchUpdatesTo(this);\n    }\n\n    protected QMUISectionDiffCallback<H, T> createDiffCallback(\n            List<QMUISection<H, T>> lastData,\n            List<QMUISection<H, T>> currentData) {\n        return new QMUISectionDiffCallback<>(lastData, currentData);\n    }\n\n    public void setCallback(Callback<H, T> callback) {\n        mCallback = callback;\n    }\n\n    void setViewCallback(ViewCallback viewCallback) {\n        mViewCallback = viewCallback;\n    }\n\n\n    public int getSectionCount() {\n        return mCurrentData.size();\n    }\n\n    public int getItemIndex(int position) {\n        if (position < 0 || position >= mItemIndex.size()) {\n            return QMUISection.ITEM_INDEX_UNKNOWN;\n        }\n        return mItemIndex.get(position);\n    }\n\n    public int getSectionIndex(int position) {\n        if (position < 0 || position >= mSectionIndex.size()) {\n            return QMUISection.SECTION_INDEX_UNKNOWN;\n        }\n        return mSectionIndex.get(position);\n    }\n\n    @Nullable\n    public QMUISection<H, T> getSection(int position) {\n        if (position < 0 || position >= mSectionIndex.size()) {\n            return null;\n        }\n        int sectionIndex = mSectionIndex.get(position);\n        if (sectionIndex < 0 || sectionIndex >= mCurrentData.size()) {\n            return null;\n        }\n        return mCurrentData.get(sectionIndex);\n    }\n\n    @Nullable\n    public QMUISection<H, T> getSectionDirectly(int index) {\n        if (index < 0 || index >= mCurrentData.size()) {\n            return null;\n        }\n        return mCurrentData.get(index);\n    }\n\n    public boolean isSectionFold(int position) {\n        QMUISection<H, T> section = getSection(position);\n        if (section == null) {\n            return false;\n        }\n        return section.isFold();\n    }\n\n    @Nullable\n    public T getSectionItem(int position) {\n        int itemIndex = getItemIndex(position);\n        if (itemIndex < 0) {\n            return null;\n        }\n        QMUISection<H, T> section = getSection(position);\n        if (section == null) {\n            return null;\n        }\n        return section.getItemAt(itemIndex);\n    }\n\n\n    public void finishLoadMore(QMUISection<H, T> section, List<T> itemList,\n                               boolean isLoadBefore, boolean existMoreData) {\n\n        if (isLoadBefore) {\n            mLoadingBeforeSections.remove(section);\n        } else {\n            mLoadingAfterSections.remove(section);\n        }\n\n        if (!mCurrentData.contains(section)) {\n            return;\n        }\n\n        // if load before, we should focus first item in section. otherwise the new data will\n        // wash current items down\n        if (isLoadBefore && !section.isFold()) {\n            for (int i = 0; i < mItemIndex.size(); i++) {\n                int itemIndex = mItemIndex.get(i);\n                if (itemIndex == 0 && section == getSection(i)) {\n                    RecyclerView.ViewHolder focusViewHolder = mViewCallback == null ? null :\n                            mViewCallback.findViewHolderForAdapterPosition(i);\n                    if (focusViewHolder != null) {\n                        mViewCallback.requestChildFocus(focusViewHolder.itemView);\n                    }\n                    break;\n                }\n\n            }\n        }\n\n        section.finishLoadMore(itemList, isLoadBefore, existMoreData);\n        lock(section);\n\n        diff(true, true);\n    }\n\n    /**\n     * lock section if needed, so we can stop scroll when in loadMore\n     *\n     * @param section\n     */\n    private void lock(QMUISection<H, T> section) {\n        boolean lockPrevious = !section.isFold() && section.isExistBeforeDataToLoad()\n                && !section.isErrorToLoadBefore();\n        boolean lockAfter = !section.isFold() && section.isExistAfterDataToLoad()\n                && !section.isErrorToLoadAfter();\n\n        int index = mCurrentData.indexOf(section);\n        if (index < 0 || index >= mCurrentData.size()) {\n            return;\n        }\n        section.setLocked(false);\n        lockBefore(index - 1, lockPrevious);\n        lockAfter(index + 1, lockAfter);\n    }\n\n    private void lockBefore(int current, boolean needLock) {\n        while (current >= 0) {\n            QMUISection<H, T> section = mCurrentData.get(current);\n            if (needLock) {\n                section.setLocked(true);\n            } else {\n                section.setLocked(false);\n                needLock = !section.isFold() && section.isExistBeforeDataToLoad()\n                        && !section.isErrorToLoadBefore();\n            }\n            current--;\n        }\n    }\n\n    private void lockAfter(int current, boolean needLock) {\n        while (current < mCurrentData.size()) {\n            QMUISection<H, T> section = mCurrentData.get(current);\n            if (needLock) {\n                section.setLocked(true);\n            } else {\n                section.setLocked(false);\n                needLock = !section.isFold() && section.isExistAfterDataToLoad()\n                        && !section.isErrorToLoadAfter();\n            }\n            current++;\n        }\n    }\n\n\n    /**\n     * scroll to special section header\n     *\n     * @param targetSection\n     * @param scrollToTop   True to scroll to recyclerView Top, false to scroll to visible area.\n     */\n    public void scrollToSectionHeader(@NonNull QMUISection<H, T> targetSection, boolean scrollToTop) {\n        if (mViewCallback == null) {\n            return;\n        }\n        for (int i = 0; i < mCurrentData.size(); i++) {\n            QMUISection<H, T> section = mCurrentData.get(i);\n            if (targetSection.getHeader().isSameItem(section.getHeader())) {\n                if (section.isLocked()) {\n                    lock(section);\n                    diff(false, true);\n                    safeScrollToSection(section, scrollToTop);\n                } else {\n                    safeScrollToSection(section, scrollToTop);\n                }\n                return;\n            }\n        }\n\n    }\n\n\n    private void safeScrollToSection(@NonNull QMUISection<H, T> targetSection, boolean scrollToTop) {\n        for (int i = 0; i < mSectionIndex.size(); i++) {\n            int sectionIndex = mSectionIndex.get(i);\n            if (sectionIndex < 0 || sectionIndex >= mCurrentData.size()) {\n                continue;\n            }\n            int itemIndex = mItemIndex.get(i);\n            if (itemIndex == ITEM_INDEX_SECTION_HEADER) {\n                QMUISection<H, T> temp = mCurrentData.get(sectionIndex);\n                if (temp.getHeader().isSameItem(targetSection.getHeader())) {\n                    mViewCallback.scrollToPosition(i, true, scrollToTop);\n                    return;\n                }\n            }\n        }\n    }\n\n\n    /**\n     * scroll to special section item\n     *\n     * @param targetSection section info. if your items are not repeated in different section,\n     *                      you can use null for this method.\n     * @param targetItem    item info\n     * @param scrollToTop   True to scroll to recyclerView Top, false to scroll to visible area.\n     */\n    public void scrollToSectionItem(@Nullable QMUISection<H, T> targetSection, @NonNull T targetItem, boolean scrollToTop) {\n        if (mViewCallback == null) {\n            return;\n        }\n        // can not trust mItemIndex, maybe the section owned this item is folded\n        // if this happened, we should unfold the section\n        for (int i = 0; i < mCurrentData.size(); i++) {\n            QMUISection<H, T> section = mCurrentData.get(i);\n            if ((targetSection == null && section.existItem(targetItem)) || targetSection == section) {\n                if (section.isFold() || section.isLocked()) {\n                    // unlock this section\n                    section.setFold(false);\n                    lock(section);\n                    diff(false, true);\n                    safeScrollToSectionItem(section, targetItem, scrollToTop);\n                } else {\n                    safeScrollToSectionItem(section, targetItem, scrollToTop);\n                }\n                return;\n            }\n        }\n    }\n\n    private void safeScrollToSectionItem(@NonNull QMUISection<H, T> targetSection, @NonNull T item, boolean scrollToTop) {\n        for (int i = 0; i < mItemIndex.size(); i++) {\n            int itemIndex = mItemIndex.get(i);\n            if (itemIndex < 0) {\n                continue;\n            }\n            QMUISection<H, T> section = getSection(i);\n            if (section != targetSection) {\n                continue;\n            }\n            if (section.getItemAt(itemIndex).isSameItem(item)) {\n                mViewCallback.scrollToPosition(i, false, scrollToTop);\n                return;\n            }\n        }\n    }\n\n    /**\n     * only for custom item\n     *\n     * @param sectionIndex\n     * @param customItemIndex\n     * @param unFoldTargetSection\n     * @return\n     */\n    public int findCustomPosition(int sectionIndex, int customItemIndex, boolean unFoldTargetSection) {\n        int itemIndex = QMUISection.ITEM_INDEX_CUSTOM_OFFSET + customItemIndex;\n        return findPosition(sectionIndex, itemIndex, unFoldTargetSection);\n    }\n\n    /**\n     * find position by sectionIndex and itemIndex\n     *\n     * @param sectionIndex\n     * @param itemIndex\n     * @param unFoldTargetSection\n     * @return\n     */\n    public int findPosition(int sectionIndex, int itemIndex, boolean unFoldTargetSection) {\n        if (unFoldTargetSection && sectionIndex >= 0) {\n            QMUISection<H, T> section = mCurrentData.get(sectionIndex);\n            if (section != null && section.isFold()) {\n                section.setFold(false);\n                lock(section);\n                diff(false, true);\n            }\n        }\n        for (int i = 0; i < getItemCount(); i++) {\n            if (mSectionIndex.get(i) != sectionIndex) {\n                continue;\n            }\n            if (mItemIndex.get(i) == itemIndex) {\n                return i;\n            }\n        }\n        return RecyclerView.NO_POSITION;\n    }\n\n    /**\n     * find position by positionFinder\n     *\n     * @param positionFinder\n     * @param unFoldTargetSection\n     * @return\n     */\n    public int findPosition(PositionFinder<H, T> positionFinder, boolean unFoldTargetSection) {\n        if (!unFoldTargetSection) {\n            for (int i = 0; i < getItemCount(); i++) {\n                QMUISection<H, T> section = getSection(i);\n                if (section == null) {\n                    continue;\n                }\n                int itemIndex = getItemIndex(i);\n                if (itemIndex == ITEM_INDEX_SECTION_HEADER) {\n                    if (positionFinder.find(section, null)) {\n                        return i;\n                    }\n                } else if (itemIndex >= 0) {\n                    if (positionFinder.find(section, section.getItemAt(itemIndex))) {\n                        return i;\n                    }\n                }\n            }\n            return RecyclerView.NO_POSITION;\n        }\n        QMUISection<H, T> targetSection = null;\n        T targetItem = null;\n        loop:\n        for (int i = 0; i < mCurrentData.size(); i++) {\n            QMUISection<H, T> section = mCurrentData.get(i);\n            if (positionFinder.find(section, null)) {\n                targetSection = section;\n                break;\n            }\n            for (int j = 0; j < section.getItemCount(); j++) {\n                if (positionFinder.find(section, section.getItemAt(j))) {\n                    targetSection = section;\n                    targetItem = section.getItemAt(j);\n                    boolean isFold = section.isFold();\n                    if (isFold) {\n                        section.setFold(false);\n                        lock(section);\n                        diff(false, true);\n                    }\n                    break loop;\n                }\n            }\n        }\n        for (int i = 0; i < getItemCount(); i++) {\n            QMUISection<H, T> section = getSection(i);\n            if (section != targetSection) {\n                continue;\n            }\n            int itemIndex = getItemIndex(i);\n            if (itemIndex == ITEM_INDEX_SECTION_HEADER && targetItem == null) {\n                return i;\n            } else if (itemIndex >= 0) {\n                if (section.getItemAt(itemIndex).isSameItem(targetItem)) {\n                    return i;\n                }\n            }\n        }\n        return RecyclerView.NO_POSITION;\n    }\n\n    public void toggleFold(int position, boolean scrollToTop) {\n        QMUISection<H, T> section = getSection(position);\n        if (section == null) {\n            return;\n        }\n        section.setFold(!section.isFold());\n        lock(section);\n        diff(false, true);\n        if (scrollToTop && !section.isFold() && mViewCallback != null) {\n            for (int i = 0; i < mSectionIndex.size(); i++) {\n                int itemIndex = getItemIndex(i);\n                if (itemIndex == ITEM_INDEX_SECTION_HEADER && getSection(i) == section) {\n                    mViewCallback.scrollToPosition(i, true, true);\n                    return;\n                }\n            }\n        }\n    }\n\n\n    public int getRelativeStickyPosition(int position) {\n        while (getItemViewType(position) != ITEM_TYPE_SECTION_HEADER) {\n            position--;\n            if (position < 0) {\n                return RecyclerView.NO_POSITION;\n            }\n        }\n        return position;\n    }\n\n    public boolean isRemoveSectionTitleIfOnlyOneSection() {\n        return mRemoveSectionTitleIfOnlyOneSection;\n    }\n\n    @Override\n    public final int getItemCount() {\n        return mItemIndex.size();\n    }\n\n    @NonNull\n    @Override\n    public final VH onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {\n        if (type == ITEM_TYPE_SECTION_HEADER) {\n            return onCreateSectionHeaderViewHolder(viewGroup);\n        } else if (type == ITEM_TYPE_SECTION_ITEM) {\n            return onCreateSectionItemViewHolder(viewGroup);\n        } else if (type == ITEM_TYPE_SECTION_LOADING) {\n            return onCreateSectionLoadingViewHolder(viewGroup);\n        } else {\n            return onCreateCustomItemViewHolder(viewGroup, type - ITEM_TYPE_CUSTOM_OFFSET);\n        }\n    }\n\n    @Override\n    public final void onBindViewHolder(@NonNull final VH vh, int position) {\n        final int stickyPosition = position;\n        QMUISection<H, T> section = getSection(position);\n        int itemIndex = getItemIndex(position);\n        if (itemIndex == ITEM_INDEX_SECTION_HEADER) {\n            onBindSectionHeader(vh, position, section);\n        } else if (itemIndex >= 0) {\n            onBindSectionItem(vh, position, section, itemIndex);\n        } else if (itemIndex == ITEM_INDEX_LOAD_BEFORE || itemIndex == ITEM_INDEX_LOAD_AFTER) {\n            onBindSectionLoadingItem(vh, position, section, itemIndex == ITEM_INDEX_LOAD_BEFORE);\n        } else {\n            onBindCustomItem(vh, position, section, itemIndex - QMUISection.ITEM_INDEX_CUSTOM_OFFSET);\n        }\n        if (itemIndex == ITEM_INDEX_LOAD_AFTER) {\n            vh.isLoadBefore = false;\n        } else if (itemIndex == ITEM_INDEX_LOAD_BEFORE) {\n            vh.isLoadBefore = true;\n        }\n        vh.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                int pos = vh.isForStickyHeader ? stickyPosition : vh.getAdapterPosition();\n                if (pos != RecyclerView.NO_POSITION && mCallback != null) {\n                    mCallback.onItemClick(vh, pos);\n                }\n            }\n        });\n        vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {\n            @Override\n            public boolean onLongClick(View v) {\n                int pos = vh.isForStickyHeader ? stickyPosition : vh.getAdapterPosition();\n                if (pos != RecyclerView.NO_POSITION && mCallback != null) {\n                    return mCallback.onItemLongClick(vh, pos);\n                }\n                return false;\n            }\n        });\n    }\n\n    @NonNull\n    protected abstract VH onCreateSectionHeaderViewHolder(@NonNull ViewGroup viewGroup);\n\n    @NonNull\n    protected abstract VH onCreateSectionItemViewHolder(@NonNull ViewGroup viewGroup);\n\n    @NonNull\n    protected abstract VH onCreateSectionLoadingViewHolder(@NonNull ViewGroup viewGroup);\n\n    @NonNull\n    protected abstract VH onCreateCustomItemViewHolder(@NonNull ViewGroup viewGroup, int type);\n\n\n    protected void onBindSectionHeader(VH holder, int position, QMUISection<H, T> section) {\n\n    }\n\n    protected void onBindSectionItem(VH holder, int position, QMUISection<H, T> section, int itemIndex) {\n\n    }\n\n    protected void onBindSectionLoadingItem(VH holder, int position, QMUISection<H, T> section, boolean loadingBefore) {\n\n    }\n\n    protected void onBindCustomItem(VH holder, int position, @Nullable QMUISection<H, T> section, int itemIndex) {\n\n    }\n\n\n    @Override\n    public final int getItemViewType(int position) {\n        int itemIndex = getItemIndex(position);\n        if (itemIndex == QMUISection.ITEM_INDEX_UNKNOWN) {\n            // QMUIStickySectionItemDecoration uses findFirstVisibleItemPosition to get the layout position\n            // it may be exceed the adapter position range if layout is not updated in time\n            Log.e(TAG, \"the item index is undefined, you may need to check your data if not called by QMUIStickySectionItemDecoration.\");\n            return ITEM_TYPE_UNKNOWN;\n        }\n        if (itemIndex == ITEM_INDEX_SECTION_HEADER) {\n            return ITEM_TYPE_SECTION_HEADER;\n        } else if (itemIndex == ITEM_INDEX_LOAD_BEFORE || itemIndex == ITEM_INDEX_LOAD_AFTER) {\n            return ITEM_TYPE_SECTION_LOADING;\n        } else if (itemIndex >= 0) {\n            return ITEM_TYPE_SECTION_ITEM;\n        } else {\n            return ITEM_TYPE_CUSTOM_OFFSET + getCustomItemViewType(itemIndex - ITEM_INDEX_CUSTOM_OFFSET, position);\n        }\n    }\n\n    @Override\n    public void onViewAttachedToWindow(@NonNull VH holder) {\n        if (holder.getItemViewType() == ITEM_TYPE_SECTION_LOADING && mCallback != null) {\n            if (!holder.isLoadError) {\n                QMUISection<H, T> section = getSection(holder.getAdapterPosition());\n                if (section != null) {\n                    if (holder.isLoadBefore) {\n                        if (mLoadingBeforeSections.contains(section)) {\n                            return;\n                        }\n                        mLoadingBeforeSections.add(section);\n                        mCallback.loadMore(section, true);\n                    } else {\n                        if (mLoadingAfterSections.contains(section)) {\n                            return;\n                        }\n                        mLoadingAfterSections.add(section);\n                        mCallback.loadMore(section, false);\n                    }\n\n                }\n            }\n        }\n    }\n\n    protected int getCustomItemViewType(int itemIndex, int position) {\n        return ITEM_TYPE_UNKNOWN;\n    }\n\n    public interface Callback<H extends QMUISection.Model<H>, T extends QMUISection.Model<T>> {\n        void loadMore(QMUISection<H, T> section, boolean loadMoreBefore);\n\n        void onItemClick(ViewHolder holder, int position);\n\n        boolean onItemLongClick(ViewHolder holder, int position);\n    }\n\n    public interface ViewCallback {\n        void scrollToPosition(int position, boolean isSectionHeader, boolean scrollToTop);\n\n        @Nullable\n        RecyclerView.ViewHolder findViewHolderForAdapterPosition(int position);\n\n        void requestChildFocus(View view);\n    }\n\n    public interface PositionFinder<H extends QMUISection.Model<H>, T extends QMUISection.Model<T>> {\n        /**\n         * if item == null, indicate this call for header.\n         *\n         * @param section\n         * @param item\n         * @return\n         */\n        boolean find(@NonNull QMUISection<H, T> section, @Nullable T item);\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n\n        public boolean isLoadError = false;\n        public boolean isLoadBefore = false;\n        public boolean isForStickyHeader = false;\n\n        public ViewHolder(View itemView) {\n            super(itemView);\n        }\n\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUIStickySectionItemDecoration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport android.graphics.Canvas;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.lang.ref.WeakReference;\n\n/**\n * Created by cgspine on 2018/1/20.\n */\n\npublic class QMUIStickySectionItemDecoration<VH extends QMUIStickySectionAdapter.ViewHolder>\n        extends RecyclerView.ItemDecoration {\n\n    private Callback<VH> mCallback;\n    private VH mStickyHeaderViewHolder;\n    private int mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n    private WeakReference<ViewGroup> mWeakSectionContainer;\n    private int mTargetTop = 0;\n\n    public QMUIStickySectionItemDecoration(ViewGroup sectionContainer, @NonNull Callback<VH> callback) {\n        mCallback = callback;\n        mWeakSectionContainer = new WeakReference<>(sectionContainer);\n\n        mCallback.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                super.onChanged();\n                mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n                mCallback.invalidate();\n            }\n\n            @Override\n            public void onItemRangeInserted(int positionStart, int itemCount) {\n                super.onItemRangeInserted(positionStart, itemCount);\n                if (positionStart <= mStickyHeaderViewPosition) {\n                    mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n                    mCallback.invalidate();\n                }\n            }\n\n            @Override\n            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {\n                super.onItemRangeMoved(fromPosition, toPosition, itemCount);\n                if (fromPosition == mStickyHeaderViewPosition ||\n                        toPosition == mStickyHeaderViewPosition) {\n                    mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n                    mCallback.invalidate();\n                }\n            }\n\n            @Override\n            public void onItemRangeChanged(int positionStart, int itemCount) {\n                // stickyViewHolder should update when the adapter updates relative view holder\n                super.onItemRangeChanged(positionStart, itemCount);\n                if (mStickyHeaderViewPosition >= positionStart\n                        && mStickyHeaderViewPosition < positionStart + itemCount\n                        && mStickyHeaderViewHolder != null\n                        && mWeakSectionContainer.get() != null) {\n                    mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n                    mCallback.invalidate();\n                }\n            }\n\n            @Override\n            public void onItemRangeRemoved(int positionStart, int itemCount) {\n                super.onItemRangeRemoved(positionStart, itemCount);\n                if (mStickyHeaderViewPosition >= positionStart\n                        && mStickyHeaderViewPosition < positionStart + itemCount) {\n                    mStickyHeaderViewPosition = RecyclerView.NO_POSITION;\n                    setHeaderVisibility(false);\n                }\n            }\n        });\n    }\n\n    private void setHeaderVisibility(boolean visibility) {\n        ViewGroup sectionContainer = mWeakSectionContainer.get();\n        if (sectionContainer == null) {\n            return;\n        }\n        sectionContainer.setVisibility(visibility ? View.VISIBLE : View.GONE);\n        mCallback.onHeaderVisibilityChanged(visibility);\n    }\n\n    public int getStickyHeaderViewPosition() {\n        return mStickyHeaderViewPosition;\n    }\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,\n                           @NonNull RecyclerView.State state) {\n\n\n        ViewGroup sectionContainer = mWeakSectionContainer.get();\n        if (sectionContainer == null) {\n            return;\n        }\n\n        if(parent.getChildCount() == 0){\n            setHeaderVisibility(false);\n        }\n\n        RecyclerView.Adapter adapter = parent.getAdapter();\n        if (adapter == null) {\n            setHeaderVisibility(false);\n            return;\n        }\n\n        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();\n        if (!(layoutManager instanceof LinearLayoutManager)) {\n            setHeaderVisibility(false);\n            return;\n        }\n        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;\n        int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();\n\n        if (firstVisibleItemPosition == RecyclerView.NO_POSITION) {\n            setHeaderVisibility(false);\n            return;\n        }\n\n        int headerPos = mCallback.getRelativeStickyItemPosition(firstVisibleItemPosition);\n        if (headerPos == RecyclerView.NO_POSITION) {\n            setHeaderVisibility(false);\n            return;\n        }\n        int itemType = mCallback.getItemViewType(headerPos);\n        if (itemType == QMUIStickySectionAdapter.ITEM_TYPE_UNKNOWN) {\n            setHeaderVisibility(false);\n            return;\n        }\n        if (mStickyHeaderViewHolder == null || mStickyHeaderViewHolder.getItemViewType() != itemType) {\n            mStickyHeaderViewHolder = createStickyViewHolder(parent, headerPos, itemType);\n        }\n\n        if (mStickyHeaderViewPosition != headerPos) {\n            mStickyHeaderViewPosition = headerPos;\n            bindStickyViewHolder(sectionContainer, mStickyHeaderViewHolder, headerPos);\n        }\n\n        setHeaderVisibility(true);\n\n        int contactPoint = sectionContainer.getHeight() - 1;\n        final View childInContact = parent.findChildViewUnder(parent.getWidth() / 2, contactPoint);\n        if (childInContact == null) {\n            mTargetTop = parent.getTop();\n            ViewCompat.offsetTopAndBottom(sectionContainer, mTargetTop - sectionContainer.getTop());\n            return;\n        }\n\n        if (mCallback.isHeaderItem(parent.getChildAdapterPosition(childInContact))) {\n            mTargetTop = childInContact.getTop() + parent.getTop() - sectionContainer.getHeight();\n            ViewCompat.offsetTopAndBottom(sectionContainer, mTargetTop - sectionContainer.getTop());\n            return;\n        }\n\n        mTargetTop = parent.getTop();\n        ViewCompat.offsetTopAndBottom(sectionContainer, mTargetTop - sectionContainer.getTop());\n    }\n\n    public int getTargetTop() {\n        return mTargetTop;\n    }\n\n\n    private VH createStickyViewHolder(RecyclerView recyclerView, int position, int itemType) {\n        VH vh = mCallback.createViewHolder(recyclerView, itemType);\n        vh.isForStickyHeader = true;\n        return vh;\n    }\n\n    private void bindStickyViewHolder(ViewGroup sectionContainer, VH viewHolder, int position) {\n        mCallback.bindViewHolder(viewHolder, position);\n        sectionContainer.removeAllViews();\n        sectionContainer.addView(viewHolder.itemView);\n    }\n\n\n    public interface Callback<ViewHolder extends QMUIStickySectionAdapter.ViewHolder> {\n        /**\n         * @param pos adapterPosition\n         * @return sticky section header position\n         */\n        int getRelativeStickyItemPosition(int pos);\n\n\n        boolean isHeaderItem(int pos);\n\n        ViewHolder createViewHolder(ViewGroup parent, int viewType);\n\n        void bindViewHolder(ViewHolder holder, int position);\n\n        int getItemViewType(int position);\n\n        void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer);\n\n        void onHeaderVisibilityChanged(boolean visible);\n\n        void invalidate();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/section/QMUIStickySectionLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.section;\n\nimport android.content.Context;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\n\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUIStickySectionLayout extends QMUIFrameLayout implements QMUIStickySectionAdapter.ViewCallback {\n\n    private RecyclerView mRecyclerView;\n    private QMUIFrameLayout mStickySectionWrapView;\n    private QMUIStickySectionItemDecoration mStickySectionItemDecoration;\n    private int mStickySectionViewHeight = -1;\n    private List<DrawDecoration> mDrawDecorations;\n    /**\n     * if scrollToPosition happened before mStickySectionWrapView finished layout,\n     * the target item may be covered by mStickySectionWrapView, so we delay to\n     * execute the scroll action\n     */\n    private Runnable mPendingScrollAction = null;\n\n    public QMUIStickySectionLayout(Context context) {\n        this(context, null);\n    }\n\n    public QMUIStickySectionLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUIStickySectionLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        mStickySectionWrapView = new QMUIFrameLayout(context);\n        mRecyclerView = new RecyclerView(context);\n        addView(mRecyclerView, new LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        addView(mStickySectionWrapView, new LayoutParams\n                (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        mStickySectionWrapView.addOnLayoutChangeListener(new OnLayoutChangeListener() {\n            @Override\n            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {\n                mStickySectionViewHeight = bottom - top;\n                if (mStickySectionViewHeight > 0 && mPendingScrollAction != null) {\n                    mPendingScrollAction.run();\n                    mPendingScrollAction = null;\n                }\n            }\n        });\n    }\n\n    public void addDrawDecoration(@NonNull DrawDecoration drawDecoration){\n        if(mDrawDecorations == null){\n            mDrawDecorations = new ArrayList<>();\n        }\n        mDrawDecorations.add(drawDecoration);\n    }\n\n    public void removeDrawDecoration(@NonNull DrawDecoration drawDecoration){\n        if(mDrawDecorations == null || mDrawDecorations.isEmpty()){\n            return;\n        }\n        mDrawDecorations.remove(drawDecoration);\n    }\n\n    public void configStickySectionWrapView(StickySectionWrapViewConfig stickySectionWrapViewConfig) {\n        if (stickySectionWrapViewConfig != null) {\n            stickySectionWrapViewConfig.config(mStickySectionWrapView);\n        }\n    }\n\n    public QMUIFrameLayout getStickySectionWrapView() {\n        return mStickySectionWrapView;\n    }\n\n    public RecyclerView getRecyclerView() {\n        return mRecyclerView;\n    }\n\n    public @Nullable\n    View getStickySectionView() {\n        if (mStickySectionWrapView.getVisibility() != View.VISIBLE\n                || mStickySectionWrapView.getChildCount() == 0) {\n            return null;\n        }\n        return mStickySectionWrapView.getChildAt(0);\n    }\n\n    public int getStickyHeaderPosition() {\n        if (mStickySectionItemDecoration == null) {\n            return RecyclerView.NO_POSITION;\n        }\n        return mStickySectionItemDecoration.getStickyHeaderViewPosition();\n    }\n\n    /**\n     * proxy to {@link RecyclerView#setLayoutManager(RecyclerView.LayoutManager)}\n     *\n     * @param layoutManager LayoutManager to use\n     */\n    public void setLayoutManager(@NonNull RecyclerView.LayoutManager layoutManager) {\n        mRecyclerView.setLayoutManager(layoutManager);\n    }\n\n    /**\n     * section header will be sticky when scrolling, see {@link #setAdapter(QMUIStickySectionAdapter, boolean)}\n     *\n     * @param adapter the adapter inherited from QMUIStickySectionAdapter\n     * @param <H>     generic parameter of QMUIStickySectionAdapter, indicating the section header\n     * @param <T>     generic parameter of QMUIStickySectionAdapter, indicating the section item\n     * @param <VH>    generic parameter of QMUIStickySectionAdapter, indicating the view holder\n     */\n    public <H extends QMUISection.Model<H>,\n            T extends QMUISection.Model<T>,\n            VH extends QMUIStickySectionAdapter.ViewHolder> void setAdapter(\n            QMUIStickySectionAdapter<H, T, VH> adapter) {\n        setAdapter(adapter, true);\n    }\n\n\n    /**\n     * set the adapter for recyclerView, the parameter sticky indicates whether\n     * the section header is sticky or not when scrolling.\n     *\n     * @param adapter the adapter inherited from QMUIStickySectionAdapter\n     * @param sticky  if true, make the section header sticky when scrolling\n     * @param <H>     generic parameter of QMUIStickySectionAdapter, indicating the section header\n     * @param <T>     generic parameter of QMUIStickySectionAdapter, indicating the section item\n     * @param <VH>    generic parameter of QMUIStickySectionAdapter, indicating the view holder\n     */\n    public <H extends QMUISection.Model<H>,\n            T extends QMUISection.Model<T>,\n            VH extends QMUIStickySectionAdapter.ViewHolder> void setAdapter(\n            final QMUIStickySectionAdapter<H, T, VH> adapter, boolean sticky) {\n        if (sticky) {\n            QMUIStickySectionItemDecoration.Callback<VH> callback = new QMUIStickySectionItemDecoration.Callback<VH>() {\n                @Override\n                public int getRelativeStickyItemPosition(int pos) {\n                    return adapter.getRelativeStickyPosition(pos);\n                }\n\n                @Override\n                public boolean isHeaderItem(int pos) {\n                    return adapter.getItemViewType(pos) == QMUIStickySectionAdapter.ITEM_TYPE_SECTION_HEADER;\n                }\n\n                @Override\n                public VH createViewHolder(ViewGroup parent, int viewType) {\n                    return adapter.createViewHolder(parent, viewType);\n                }\n\n                @Override\n                public void bindViewHolder(VH holder, int position) {\n                    adapter.bindViewHolder(holder, position);\n                }\n\n                @Override\n                public int getItemViewType(int position) {\n                    return adapter.getItemViewType(position);\n                }\n\n                @Override\n                public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {\n                    adapter.registerAdapterDataObserver(observer);\n                }\n\n                @Override\n                public void onHeaderVisibilityChanged(boolean visible) {\n\n                }\n\n                @Override\n                public void invalidate() {\n                    mRecyclerView.invalidate();\n                }\n            };\n            mStickySectionItemDecoration = new QMUIStickySectionItemDecoration<>(mStickySectionWrapView, callback);\n            mRecyclerView.addItemDecoration(mStickySectionItemDecoration);\n        }\n\n\n        adapter.setViewCallback(this);\n        mRecyclerView.setAdapter(adapter);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (mStickySectionItemDecoration != null) {\n            mStickySectionWrapView.layout(mStickySectionWrapView.getLeft(),\n                    mStickySectionItemDecoration.getTargetTop(),\n                    mStickySectionWrapView.getRight(),\n                    mStickySectionItemDecoration.getTargetTop() + mStickySectionWrapView.getHeight());\n        }\n    }\n\n    @Override\n    public void scrollToPosition(final int position, boolean isSectionHeader, final boolean scrollToTop) {\n        mPendingScrollAction = null;\n        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();\n        if (adapter == null || position < 0 || position >= adapter.getItemCount()) {\n            return;\n        }\n\n        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;\n            int firstVPos = linearLayoutManager.findFirstCompletelyVisibleItemPosition();\n            int lastVPos = linearLayoutManager.findLastCompletelyVisibleItemPosition();\n            int offset = 0;\n            if (!isSectionHeader) {\n                if (mStickySectionViewHeight <= 0) {\n                    // delay to re scroll\n                    mPendingScrollAction = new Runnable() {\n                        @Override\n                        public void run() {\n                            scrollToPosition(position, false, scrollToTop);\n                        }\n                    };\n                }\n                offset = mStickySectionWrapView.getHeight();\n            }\n            if (position < firstVPos + 1 /* increase one to avoid being covered */ || position > lastVPos || scrollToTop) {\n                linearLayoutManager.scrollToPositionWithOffset(position, offset);\n            }\n        } else {\n            mRecyclerView.scrollToPosition(position);\n        }\n    }\n\n    @Nullable\n    @Override\n    public RecyclerView.ViewHolder findViewHolderForAdapterPosition(int position) {\n        return mRecyclerView.findViewHolderForAdapterPosition(position);\n    }\n\n    @Override\n    public void requestChildFocus(View view) {\n        mRecyclerView.requestChildFocus(view, null);\n    }\n\n    public interface StickySectionWrapViewConfig {\n        void config(QMUIFrameLayout stickySectionWrapView);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        if(mDrawDecorations != null){\n            for(DrawDecoration drawDecoration: mDrawDecorations){\n                drawDecoration.onDraw(canvas, this);\n            }\n        }\n        super.dispatchDraw(canvas);\n        if(mDrawDecorations != null){\n            for(DrawDecoration drawDecoration: mDrawDecorations){\n                drawDecoration.onDrawOver(canvas, this);\n            }\n        }\n    }\n\n    @Override\n    public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {\n        super.onDescendantInvalidated(child, target);\n        if(target == mRecyclerView && mDrawDecorations != null && !mDrawDecorations.isEmpty()){\n            invalidate();\n        }\n    }\n\n    public interface DrawDecoration {\n        void onDraw(@NonNull Canvas c, @NonNull QMUIStickySectionLayout parent);\n        void onDrawOver(@NonNull Canvas c, @NonNull QMUIStickySectionLayout parent);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUIBasicTabSegment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.animation.Animator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\n\nimport com.qmuiteam.qmui.QMUIInterpolatorStaticHolder;\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.layout.IQMUILayout;\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.IQMUISkinDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUIColorHelper;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n/**\n * <p>用于横向多个 Tab 的布局，可以灵活配置 Tab</p>\n * <ul>\n * <li>可以用 xml 和 QMUITabSegment 提供的 set 方法统一配置文字颜色、icon 位置、是否要下划线等</li>\n * <li>每个 Tab 都可以非常灵活的配置，如果没有提供相关配置，则使用 QMUIBasicTabSegment 提供的配置，具体参考 {@link QMUITab}</li>\n * </ul>\n * <p>\n * <h3>使用case: </h3>\n * <ul>\n * <li>\n * 如果你希望自己设置 Tab 的文案或图片，那么通过{@link #addTab(QMUITab)}添加 Tab:\n * <code>\n * QMUIBasicTabSegment mTabSegment = new QMUIBasicTabSegment((getContext());\n * // config mTabSegment\n * QMUITabBuilder tabBuilder = mTabSegment.tabBuilder()\n * mTabSegment.addTab(tabBuilder.setText(\"item 1\").build());\n * mTabSegment.addTab(tabBuilder.setText(\"item 2\").build());\n * mTabSegment.notifyDataChanged();\n * </code>\n * </li>\n * <li>\n * 如果你想更改tab,则调用{@link #updateTabText(int, String)} 或者 {@link #replaceTab(int, QMUITab)}\n * <code>\n * mTabSegment.updateTabText(1, \"update item content\");\n * mTabSegment.replaceTab(1, tabBuilder.setText(\"replace item\").build());\n * </code>\n * </li>\n * <li>\n * 如果你想更换全部Tab,需要在addTab前调用{@link #reset()}进行重置，addTab后调用{@link #notifyDataChanged()} 将数据应用到View上：\n * <code>\n * mTabSegment.reset();\n * // update mTabSegment with new config\n * QMUITabBuilder tabBuilder = mTabSegment.tabBuilder()\n * mTabSegment.addTab(tabBuilder.setText(\"new item 1\").build());\n * mTabSegment.addTab(tabBuilder.setText(\"new item 1\").build());\n * mTabSegment.notifyDataChanged();\n * </code>\n * </li>\n * </ul>\n *\n * @author cginechen\n * @date 2016-01-27\n */\npublic class QMUIBasicTabSegment extends HorizontalScrollView implements IQMUILayout, IQMUISkinHandlerView, IQMUISkinDefaultAttrProvider {\n\n    private static final String TAG = \"QMUIBasicTabSegment\";\n\n    // mode: wrap content and scroll / match parent and avg item width\n    public static final int MODE_SCROLLABLE = 0;\n    public static final int MODE_FIXED = 1;\n    public static final int NO_POSITION = -1;\n\n\n    private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();\n    private Container mContentLayout;\n\n    protected int mCurrentSelectedIndex = NO_POSITION;\n    protected int mPendingSelectedIndex = NO_POSITION;\n\n    private QMUITabIndicator mIndicator = null;\n    private boolean mHideIndicatorWhenTabCountLessTwo = true;\n\n    /**\n     * TabSegmentMode\n     */\n    @Mode\n    private int mMode = MODE_FIXED;\n    /**\n     * item gap in MODE_SCROLLABLE\n     */\n    private int mItemSpaceInScrollMode;\n\n\n    private QMUITabAdapter mTabAdapter;\n\n    protected QMUITabBuilder mTabBuilder;\n\n    private boolean mSelectNoAnimation;\n    protected Animator mSelectAnimator;\n    private OnTabClickListener mOnTabClickListener;\n\n    private boolean mIsInSelectTab = false;\n    private QMUILayoutHelper mLayoutHelper;\n    private static SimpleArrayMap<String, Integer> sDefaultSkinAttrs;\n\n    static {\n        sDefaultSkinAttrs = new SimpleArrayMap<>(3);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BOTTOM_SEPARATOR, R.attr.qmui_skin_support_tab_separator_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.TOP_SEPARATOR, R.attr.qmui_skin_support_tab_separator_color);\n        sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_tab_bg);\n    }\n\n    public QMUIBasicTabSegment(Context context) {\n        this(context, null);\n    }\n\n    public QMUIBasicTabSegment(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.QMUITabSegmentStyle);\n    }\n\n    public QMUIBasicTabSegment(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setWillNotDraw(false);\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n        init(context, attrs, defStyleAttr);\n        setHorizontalScrollBarEnabled(false);\n        setClipToPadding(false);\n        setClipChildren(false);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n\n        TypedArray array = context.obtainStyledAttributes(attrs,\n                R.styleable.QMUITabSegment, defStyleAttr, 0);\n\n        // indicator\n        boolean hasIndicator = array.getBoolean(R.styleable.QMUITabSegment_qmui_tab_has_indicator, false);\n        int indicatorHeight = array.getDimensionPixelSize(\n                R.styleable.QMUITabSegment_qmui_tab_indicator_height,\n                getResources().getDimensionPixelSize(R.dimen.qmui_tab_segment_indicator_height));\n        boolean indicatorTop = array.getBoolean(R.styleable.QMUITabSegment_qmui_tab_indicator_top, false);\n        boolean indicatorWidthFollowContent = array.getBoolean(\n                R.styleable.QMUITabSegment_qmui_tab_indicator_with_follow_content, false);\n        mIndicator = createTabIndicatorFromXmlInfo(hasIndicator, indicatorHeight,\n                indicatorTop, indicatorWidthFollowContent);\n\n        // tabBuilder\n        int normalTextSize = array.getDimensionPixelSize(\n                R.styleable.QMUITabSegment_android_textSize,\n                getResources().getDimensionPixelSize(R.dimen.qmui_tab_segment_text_size));\n        normalTextSize = array.getDimensionPixelSize(\n                R.styleable.QMUITabSegment_qmui_tab_normal_text_size, normalTextSize);\n        int selectedTextSize = normalTextSize;\n        selectedTextSize = array.getDimensionPixelSize(\n                R.styleable.QMUITabSegment_qmui_tab_selected_text_size, selectedTextSize);\n        mTabBuilder = new QMUITabBuilder(context)\n                .setTextSize(normalTextSize, selectedTextSize)\n                .setIconPosition(array.getInt(R.styleable.QMUITabSegment_qmui_tab_icon_position,\n                        QMUITab.ICON_POSITION_LEFT));\n        mMode = array.getInt(R.styleable.QMUITabSegment_qmui_tab_mode, MODE_FIXED);\n        mItemSpaceInScrollMode = array.getDimensionPixelSize(\n                R.styleable.QMUITabSegment_qmui_tab_space, QMUIDisplayHelper.dp2px(context, 10));\n        mSelectNoAnimation = array.getBoolean(R.styleable.QMUITabSegment_qmui_tab_select_no_animation, false);\n        array.recycle();\n\n\n        mContentLayout = new Container(context);\n        addView(mContentLayout, new LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        mTabAdapter = createTabAdapter(mContentLayout);\n    }\n\n    public void setDefaultTextSize(int normalTextSize, int selectedTextSize) {\n        mTabBuilder.setTextSize(normalTextSize, selectedTextSize);\n    }\n\n    public void setDefaultTabIconPosition(@QMUITab.IconPosition int iconPosition) {\n        mTabBuilder.setIconPosition(iconPosition);\n    }\n\n    public void updateParentTabBuilder(TabBuilderUpdater updater) {\n        updater.update(mTabBuilder);\n    }\n\n    protected QMUITabAdapter createTabAdapter(ViewGroup tabParentView) {\n        return new QMUITabAdapter(this, tabParentView);\n    }\n\n    protected QMUITabIndicator createTabIndicatorFromXmlInfo(boolean hasIndicator,\n                                                             int indicatorHeight,\n                                                             boolean indicatorTop,\n                                                             boolean indicatorWidthFollowContent) {\n        if (!hasIndicator) {\n            return null;\n        }\n        return new QMUITabIndicator(indicatorHeight, indicatorTop, indicatorWidthFollowContent);\n    }\n\n    public QMUITabBuilder tabBuilder() {\n        // do not change mTabBuilder to keep common config not changed\n        return new QMUITabBuilder(mTabBuilder);\n    }\n\n    /**\n     * replace with custom indicator\n     *\n     * @param indicator if null, present there is not a indicator\n     */\n    public void setIndicator(@Nullable QMUITabIndicator indicator) {\n        mIndicator = indicator;\n        mContentLayout.requestLayout();\n    }\n\n    public void setHideIndicatorWhenTabCountLessTwo(boolean hideIndicatorWhenTabCountLessTwo) {\n        mHideIndicatorWhenTabCountLessTwo = hideIndicatorWhenTabCountLessTwo;\n    }\n\n    public void setItemSpaceInScrollMode(int itemSpaceInScrollMode) {\n        mItemSpaceInScrollMode = itemSpaceInScrollMode;\n    }\n\n    /**\n     * clear all tabs\n     */\n    public void reset() {\n        mTabAdapter.clear();\n        mCurrentSelectedIndex = NO_POSITION;\n        if (mSelectAnimator != null) {\n            mSelectAnimator.cancel();\n            mSelectAnimator = null;\n        }\n    }\n\n    /**\n     * clear select info\n     */\n    public void resetSelect() {\n        mCurrentSelectedIndex = NO_POSITION;\n        mPendingSelectedIndex = NO_POSITION;\n        if (mSelectAnimator != null) {\n            mSelectAnimator.cancel();\n            mSelectAnimator = null;\n        }\n    }\n\n\n    /**\n     * add a tab to QMUITabSegment\n     *\n     * @param tab QMUITab\n     * @return return this to chain\n     */\n    public QMUIBasicTabSegment addTab(QMUITab tab) {\n        mTabAdapter.addItem(tab);\n        return this;\n    }\n\n\n    /**\n     * notify dataChanged event to QMUITabSegment\n     */\n    public void notifyDataChanged() {\n        int current = mCurrentSelectedIndex;\n        if(mPendingSelectedIndex != NO_POSITION){\n            current = mPendingSelectedIndex;\n        }\n        resetSelect();\n        mTabAdapter.setup();\n        selectTab(current);\n    }\n\n\n    public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {\n        if (!mSelectedListeners.contains(listener)) {\n            mSelectedListeners.add(listener);\n        }\n    }\n\n    public void removeOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {\n        mSelectedListeners.remove(listener);\n    }\n\n    public void clearOnTabSelectedListeners() {\n        mSelectedListeners.clear();\n    }\n\n    public int getMode() {\n        return mMode;\n    }\n\n    public void setMode(@Mode int mode) {\n        if (mMode != mode) {\n            mMode = mode;\n            if (mode == MODE_SCROLLABLE) {\n                mTabBuilder.setGravity(Gravity.LEFT);\n            }\n            mContentLayout.invalidate();\n        }\n    }\n\n\n    protected void onClickTab(QMUITabView view, int index) {\n        if (mSelectAnimator != null || needPreventEvent()) {\n            return;\n        }\n\n        if (mOnTabClickListener != null) {\n            if (mOnTabClickListener.onTabClick(view, index)) {\n                return;\n            }\n        }\n\n        QMUITab model = mTabAdapter.getItem(index);\n        if (model != null) {\n            selectTab(index, mSelectNoAnimation, true);\n        }\n    }\n\n    protected boolean needPreventEvent() {\n        return false;\n    }\n\n\n    void onDoubleClick(int index) {\n        if (mSelectedListeners.isEmpty()) {\n            return;\n        }\n        QMUITab model = mTabAdapter.getItem(index);\n        if (model != null) {\n            dispatchTabDoubleTap(index);\n        }\n    }\n\n\n    private void dispatchTabSelected(int index) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabSelected(index);\n        }\n    }\n\n    private void dispatchTabUnselected(int index) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabUnselected(index);\n        }\n    }\n\n    private void dispatchTabReselected(int index) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabReselected(index);\n        }\n    }\n\n    private void dispatchTabDoubleTap(int index) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onDoubleTap(index);\n        }\n    }\n\n    public void setSelectNoAnimation(boolean noAnimation) {\n        mSelectNoAnimation = noAnimation;\n    }\n\n    public void selectTab(int index) {\n        selectTab(index, mSelectNoAnimation, false);\n    }\n\n    public void selectTab(final int index, boolean noAnimation, boolean fromTabClick) {\n        if (mIsInSelectTab) {\n            return;\n        }\n        mIsInSelectTab = true;\n\n        List<QMUITabView> listViews = mTabAdapter.getViews();\n\n        if (listViews.size() != mTabAdapter.getSize()) {\n            mTabAdapter.setup();\n            listViews = mTabAdapter.getViews();\n        }\n\n        if (listViews.size() == 0 || listViews.size() <= index) {\n            mIsInSelectTab = false;\n            return;\n        }\n\n        if (mSelectAnimator != null || needPreventEvent()) {\n            mPendingSelectedIndex = index;\n            mIsInSelectTab = false;\n            return;\n        }\n\n        if (mCurrentSelectedIndex == index) {\n            if (fromTabClick) {\n                // dispatch re select only when click tab\n                dispatchTabReselected(index);\n            }\n            mIsInSelectTab = false;\n            // invalidate mContentLayout to sure indicator is drawn if needed\n            mContentLayout.invalidate();\n            return;\n        }\n\n\n        if (mCurrentSelectedIndex > listViews.size()) {\n            Log.i(TAG, \"selectTab: current selected index is bigger than views size.\");\n            mCurrentSelectedIndex = NO_POSITION;\n        }\n\n        // first time to select\n        if (mCurrentSelectedIndex == NO_POSITION) {\n            QMUITab model = mTabAdapter.getItem(index);\n            layoutIndicator(model, true);\n            \n            QMUITabView tabView = listViews.get(index);\n            tabView.setSelected(true); // 标记选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n            tabView.setSelectFraction(1f);\n            \n            dispatchTabSelected(index);\n            mCurrentSelectedIndex = index;\n            mIsInSelectTab = false;\n            return;\n        }\n\n        final int prev = mCurrentSelectedIndex;\n        final QMUITab prevModel = mTabAdapter.getItem(prev);\n        final QMUITabView prevView = listViews.get(prev);\n        final QMUITab nowModel = mTabAdapter.getItem(index);\n        final QMUITabView nowView = listViews.get(index);\n\n        if (noAnimation) {\n            dispatchTabUnselected(prev);\n            dispatchTabSelected(index);\n            prevView.setSelectFraction(0f);\n            prevView.setSelected(false); // 标记未选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n            nowView.setSelectFraction(1f);\n            nowView.setSelected(true); // 标记选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n            if (mMode == MODE_SCROLLABLE) {\n                int scrollX = getScrollX(),\n                        w = getWidth(),\n                        cw = mContentLayout.getWidth(),\n                        nl = nowView.getLeft(),\n                        nw = nowView.getWidth();\n                int paddingHor = getPaddingLeft() + getPaddingRight();\n                int size = mTabAdapter.getSize();\n                int maxScrollX = cw - w + paddingHor;\n                if (index > prev) {\n                    if (index >= size - 2) {\n                        smoothScrollBy(maxScrollX - scrollX, 0);\n                    } else {\n                        int nextWidth = listViews.get(index + 1).getWidth();\n                        int targetScrollX = Math.min(maxScrollX, nl - (w - getPaddingRight() * 2 - nextWidth - nw - mItemSpaceInScrollMode));\n                        targetScrollX -= nextWidth - nw;\n                        if (scrollX < targetScrollX) {\n                            smoothScrollBy(targetScrollX - scrollX, 0);\n                        }\n                    }\n                } else {\n                    if (index <= 1) {\n                        smoothScrollBy(-scrollX, 0);\n                    } else {\n                        int prevWidth = listViews.get(index - 1).getWidth();\n                        int targetScrollX = Math.max(0, nl - prevWidth - mItemSpaceInScrollMode);\n                        if (targetScrollX < scrollX) {\n                            smoothScrollBy(targetScrollX - scrollX, 0);\n                        }\n                    }\n                }\n            }\n\n            mCurrentSelectedIndex = index;\n            mIsInSelectTab = false;\n            layoutIndicator(nowModel, true);\n            return;\n        }\n\n        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);\n        animator.setInterpolator(QMUIInterpolatorStaticHolder.LINEAR_INTERPOLATOR);\n        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float animValue = (float) animation.getAnimatedValue();\n                prevView.setSelectFraction(1 - animValue);\n                nowView.setSelectFraction(animValue);\n                layoutIndicatorInTransition(prevModel, nowModel, animValue);\n            }\n        });\n        animator.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n                mSelectAnimator = animation;\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                prevView.setSelectFraction(0f);\n                prevView.setSelected(false); // 标记未选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n                nowView.setSelectFraction(1f);\n                nowView.setSelected(true); // 标记选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n                mSelectAnimator = null;\n                // set current selected index first, dispatchTabSelected may call selectTab again.\n                mCurrentSelectedIndex = index;\n                dispatchTabSelected(index);\n                dispatchTabUnselected(prev);\n                if (mPendingSelectedIndex != NO_POSITION && !needPreventEvent()) {\n                    selectTab(mPendingSelectedIndex, true, false);\n                    mPendingSelectedIndex = NO_POSITION;\n                }\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n                mSelectAnimator = null;\n                prevView.setSelectFraction(1f);\n                prevView.setSelected(true); // 标记选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n                nowView.setSelectFraction(0f);\n                nowView.setSelected(false); // 标记未选中，使得TalkBack等屏幕阅读器可向用户报告tab状态\n                layoutIndicator(prevModel, true);\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        });\n        animator.setDuration(200);\n        animator.start();\n        mIsInSelectTab = false;\n    }\n\n    private void layoutIndicator(QMUITab model, boolean invalidate) {\n        if (model == null || mIndicator == null) {\n            return;\n        }\n        mIndicator.updateInfo(model.contentLeft, model.contentWidth, model.selectedColorAttr == 0 ? model.selectColor : QMUISkinHelper.getSkinColor(this, model.selectedColorAttr), 0f);\n        if (invalidate) {\n            mContentLayout.invalidate();\n        }\n    }\n\n    private void layoutIndicatorInTransition(QMUITab preModel, QMUITab targetModel, float offsetPercent) {\n        if (mIndicator == null) {\n            return;\n        }\n        final int leftDistance = targetModel.contentLeft - preModel.contentLeft;\n        final int widthDistance = targetModel.contentWidth - preModel.contentWidth;\n        final int targetLeft = (int) (preModel.contentLeft + leftDistance * offsetPercent);\n        final int targetWidth = (int) (preModel.contentWidth + widthDistance * offsetPercent);\n        int indicatorColor = QMUIColorHelper.computeColor(\n                preModel.selectedColorAttr == 0 ? preModel.selectColor : QMUISkinHelper.getSkinColor(this, preModel.selectedColorAttr),\n                targetModel.selectedColorAttr == 0 ? targetModel.selectColor : QMUISkinHelper.getSkinColor(this, targetModel.selectedColorAttr),\n                offsetPercent);\n        mIndicator.updateInfo(targetLeft, targetWidth, indicatorColor, offsetPercent);\n        mContentLayout.invalidate();\n    }\n\n    public void updateIndicatorPosition(final int index, float offsetPercent) {\n        if (mSelectAnimator != null || mIsInSelectTab || offsetPercent == 0) {\n            return;\n        }\n\n        int targetIndex;\n        if (offsetPercent < 0) {\n            targetIndex = index - 1;\n            offsetPercent = -offsetPercent;\n        } else {\n            targetIndex = index + 1;\n        }\n\n        final List<QMUITabView> listViews = mTabAdapter.getViews();\n        if (listViews.size() <= index || listViews.size() <= targetIndex) {\n            return;\n        }\n        QMUITab preModel = mTabAdapter.getItem(index);\n        QMUITab targetModel = mTabAdapter.getItem(targetIndex);\n        QMUITabView preView = listViews.get(index);\n        QMUITabView targetView = listViews.get(targetIndex);\n        preView.setSelectFraction(1 - offsetPercent);\n        targetView.setSelectFraction(offsetPercent);\n        layoutIndicatorInTransition(preModel, targetModel, offsetPercent);\n    }\n\n    /**\n     * 改变 Tab 的文案\n     *\n     * @param index Tab 的 index\n     * @param text  新文案\n     */\n    public void updateTabText(int index, String text) {\n        QMUITab model = mTabAdapter.getItem(index);\n        if (model == null) {\n            return;\n        }\n        model.setText(text);\n        notifyDataChanged();\n    }\n\n    /**\n     * 整个 Tab 替换\n     *\n     * @param index 需要被替换的 Tab 的 index\n     * @param model 新的 Tab\n     */\n    public void replaceTab(int index, QMUITab model) {\n        try {\n            if (mCurrentSelectedIndex == index) {\n                // re select\n                mCurrentSelectedIndex = NO_POSITION;\n            }\n            mTabAdapter.replaceItem(index, model);\n            notifyDataChanged();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void setOnTabClickListener(OnTabClickListener onTabClickListener) {\n        mOnTabClickListener = onTabClickListener;\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n\n        if (getChildCount() > 0) {\n            final View child = getChildAt(0);\n            int paddingHor = getPaddingLeft() + getPaddingRight();\n            child.measure(MeasureSpec.makeMeasureSpec(widthSize - paddingHor, MeasureSpec.EXACTLY), heightMeasureSpec);\n            if (widthMode == MeasureSpec.AT_MOST) {\n                setMeasuredDimension(Math.min(widthSize, child.getMeasuredWidth() + paddingHor), heightMeasureSpec);\n                return;\n            }\n        }\n        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);\n    }\n\n\n    public int getSelectedIndex() {\n        return mCurrentSelectedIndex;\n    }\n\n    public int getTabCount() {\n        return mTabAdapter.getSize();\n    }\n\n    /**\n     * get {@link QMUITab} by index\n     *\n     * @param index index\n     * @return QMUITab\n     */\n    public QMUITab getTab(int index) {\n        return mTabAdapter.getItem(index);\n    }\n\n\n    /**\n     * show signCount/redPoint by index\n     *\n     * @param index the index of tab\n     * @param count if count > 0, show signCount; else if count == 0 show redPoint; else show nothing\n     */\n    public void showSignCountView(Context context, int index, int count) {\n        QMUITab tab = mTabAdapter.getItem(index);\n        tab.setSignCount(count);\n        notifyDataChanged();\n    }\n\n    /**\n     * clear signCount/redPoint by index\n     *\n     * @param index the index of tab\n     */\n    public void clearSignCountView(int index) {\n        QMUITab tab = mTabAdapter.getItem(index);\n        tab.clearSignCountOrRedPoint();\n        notifyDataChanged();\n    }\n\n    /**\n     * get sign count by index\n     *\n     * @param index the index of tab\n     */\n    public int getSignCount(int index) {\n        QMUITab tab = mTabAdapter.getItem(index);\n        return tab.getSignCount();\n    }\n\n    /**\n     * is redPoint showing ?\n     *\n     * @param index the index of tab\n     * @return true if redPoint is showing\n     */\n    public boolean isRedPointShowing(int index) {\n        return mTabAdapter.getItem(index).isRedPointShowing();\n    }\n\n    @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Mode {\n    }\n\n\n    public interface OnTabClickListener {\n        /**\n         * 当某个 Tab 被点击时会触发\n         *\n         * @param tabView 被点击的View\n         * @param index 被点击的 Tab 下标\n         *\n         * @return true 拦截 selectTab 事件\n         */\n        boolean onTabClick(QMUITabView tabView, int index);\n    }\n\n    public interface OnTabSelectedListener {\n        /**\n         * 当某个 Tab 被选中时会触发\n         *\n         * @param index 被选中的 Tab 下标\n         */\n        void onTabSelected(int index);\n\n        /**\n         * 当某个 Tab 被取消选中时会触发\n         *\n         * @param index 被取消选中的 Tab 下标\n         */\n        void onTabUnselected(int index);\n\n        /**\n         * 当某个 Tab 处于被选中状态下再次被点击时会触发\n         *\n         * @param index 被再次点击的 Tab 下标\n         */\n        void onTabReselected(int index);\n\n        /**\n         * 当某个 Tab 被双击时会触发\n         *\n         * @param index 被双击的 Tab 下标\n         */\n        void onDoubleTap(int index);\n    }\n\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        super.onLayout(changed, l, t, r, b);\n        if (mCurrentSelectedIndex != NO_POSITION && mMode == MODE_SCROLLABLE) {\n            final QMUITabView view = mTabAdapter.getViews().get(mCurrentSelectedIndex);\n            if (getScrollX() > view.getLeft()) {\n                scrollTo(view.getLeft(), 0);\n            } else {\n                int realWidth = getWidth() - getPaddingRight() - getPaddingLeft();\n                if (getScrollX() + realWidth < view.getRight()) {\n                    scrollBy(view.getRight() - realWidth - getScrollX(), 0);\n                }\n            }\n        }\n    }\n\n\n    private final class Container extends ViewGroup {\n\n        public Container(Context context) {\n            super(context);\n            setClipChildren(false);\n            setWillNotDraw(false);\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\n            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);\n            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);\n            List<QMUITabView> childViews = mTabAdapter.getViews();\n            int size = childViews.size();\n            int i;\n\n            int visibleChild = 0;\n            for (i = 0; i < size; i++) {\n                View child = childViews.get(i);\n                if (child.getVisibility() == VISIBLE) {\n                    visibleChild++;\n                }\n            }\n            if (size == 0 || visibleChild == 0) {\n                setMeasuredDimension(widthSpecSize, heightSpecSize);\n                return;\n            }\n\n            int childHeight = heightSpecSize - getPaddingTop() - getPaddingBottom();\n            int childWidthMeasureSpec, childHeightMeasureSpec, resultWidthSize = 0;\n            if (mMode == MODE_FIXED) {\n                resultWidthSize = widthSpecSize;\n                int modeFixItemWidth = widthSpecSize / visibleChild;\n                for (i = 0; i < size; i++) {\n                    final View child = childViews.get(i);\n                    if (child.getVisibility() != VISIBLE) {\n                        continue;\n                    }\n                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(modeFixItemWidth, MeasureSpec.EXACTLY);\n                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);\n                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n\n                    // reset\n                    QMUITab tab = mTabAdapter.getItem(i);\n                    tab.leftAddonMargin = 0;\n                    tab.rightAddonMargin = 0;\n                }\n            } else {\n                float totalWeight = 0;\n                for (i = 0; i < size; i++) {\n                    final View child = childViews.get(i);\n                    if (child.getVisibility() != VISIBLE) {\n                        continue;\n                    }\n                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.AT_MOST);\n                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);\n                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n                    resultWidthSize += child.getMeasuredWidth() + mItemSpaceInScrollMode;\n\n                    QMUITab tab = mTabAdapter.getItem(i);\n                    totalWeight += tab.leftSpaceWeight + tab.rightSpaceWeight;\n\n                    // reset first\n                    tab.leftAddonMargin = 0;\n                    tab.rightAddonMargin = 0;\n                }\n\n                resultWidthSize -= mItemSpaceInScrollMode;\n\n                if (totalWeight > 0 && resultWidthSize < widthSpecSize) {\n                    int remain = widthSpecSize - resultWidthSize;\n                    resultWidthSize = widthSpecSize;\n                    for (i = 0; i < size; i++) {\n                        final View child = childViews.get(i);\n                        if (child.getVisibility() != VISIBLE) {\n                            continue;\n                        }\n                        QMUITab tab = mTabAdapter.getItem(i);\n                        tab.leftAddonMargin = (int) (remain * tab.leftSpaceWeight / totalWeight);\n                        tab.rightAddonMargin = (int) (remain * tab.rightSpaceWeight / totalWeight);\n                    }\n                }\n            }\n\n            setMeasuredDimension(resultWidthSize, heightSpecSize);\n        }\n\n        @Override\n        protected void onLayout(boolean changed, int l, int t, int r, int b) {\n            List<QMUITabView> childViews = mTabAdapter.getViews();\n            int size = childViews.size();\n            int i;\n            int visibleChild = 0;\n            for (i = 0; i < size; i++) {\n                View child = childViews.get(i);\n                if (child.getVisibility() == VISIBLE) {\n                    visibleChild++;\n                }\n            }\n\n            if (size == 0 || visibleChild == 0) {\n                return;\n            }\n\n            int usedLeft = getPaddingLeft();\n            for (i = 0; i < size; i++) {\n                QMUITabView childView = childViews.get(i);\n                if (childView.getVisibility() != VISIBLE) {\n                    continue;\n                }\n                final int childMeasureWidth = childView.getMeasuredWidth();\n                QMUITab model = mTabAdapter.getItem(i);\n                usedLeft += model.leftAddonMargin;\n                childView.layout(usedLeft, getPaddingTop(),\n                        usedLeft + childMeasureWidth, b - t - getPaddingBottom());\n\n\n                int oldLeft, oldWidth, newLeft, newWidth;\n                oldLeft = model.contentLeft;\n                oldWidth = model.contentWidth;\n                if (mMode == MODE_FIXED && (mIndicator != null && mIndicator.isIndicatorWidthFollowContent())) {\n                    newLeft = usedLeft + childView.getContentViewLeft();\n                    newWidth = childView.getContentViewWidth();\n                } else {\n                    newLeft = usedLeft;\n                    newWidth = childMeasureWidth;\n                }\n                if (oldLeft != newLeft || oldWidth != newWidth) {\n                    model.contentLeft = newLeft;\n                    model.contentWidth = newWidth;\n                }\n                usedLeft = usedLeft + childMeasureWidth + model.rightAddonMargin +\n                        (mMode == MODE_SCROLLABLE ? mItemSpaceInScrollMode : 0);\n            }\n\n            if (mCurrentSelectedIndex != NO_POSITION && mSelectAnimator == null\n                    && !needPreventEvent()) {\n                layoutIndicator(mTabAdapter.getItem(mCurrentSelectedIndex), false);\n            }\n        }\n\n        @Override\n        protected void onDraw(Canvas canvas) {\n            super.onDraw(canvas);\n            if (mIndicator != null && (!mHideIndicatorWhenTabCountLessTwo || mTabAdapter.getSize() > 1)) {\n                mIndicator.draw(this, canvas, getPaddingTop(), getHeight() - getPaddingBottom());\n            }\n        }\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n        super.onDraw(canvas);\n    }\n\n    @Override\n    public SimpleArrayMap<String, Integer> getDefaultSkinAttrs() {\n        return sDefaultSkinAttrs;\n    }\n\n    @Override\n    public void handle(@NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme, @Nullable SimpleArrayMap<String, Integer> attrs) {\n        manager.defaultHandleSkinAttrs(this, theme, attrs);\n        if (mIndicator != null) {\n            mIndicator.handleSkinChange(manager, skinIndex, theme, mTabAdapter.getItem(mCurrentSelectedIndex));\n            mContentLayout.invalidate();\n        }\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n\n    public interface TabBuilderUpdater {\n        void update(QMUITabBuilder builder);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITab.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.graphics.Typeface;\nimport android.view.Gravity;\nimport android.view.View;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\npublic class QMUITab {\n    public static final int ICON_POSITION_LEFT = 0;\n    public static final int ICON_POSITION_TOP = 1;\n    public static final int ICON_POSITION_RIGHT = 2;\n    public static final int ICON_POSITION_BOTTOM = 3;\n\n    public static final int SIGN_COUNT_VERTICAL_ALIGN_BOTTOM_TO_CONTENT_TOP = 0;\n    public static final int SIGN_COUNT_VERTICAL_ALIGN_TOP_TO_CONTENT_TOP = 1;\n    public static final int SIGN_COUNT_VERTICAL_ALIGN_MIDDLE_TO_CONTENT = 2;\n\n    public static final int NO_SIGN_COUNT_AND_RED_POINT = 0;\n    public static final int RED_POINT_SIGN_COUNT = -1;\n\n    @IntDef(value = {\n            ICON_POSITION_LEFT,\n            ICON_POSITION_TOP,\n            ICON_POSITION_RIGHT,\n            ICON_POSITION_BOTTOM})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface IconPosition {\n    }\n\n\n    boolean allowIconDrawOutside;\n    int iconTextGap;\n    int normalTextSize;\n    int selectedTextSize;\n    Typeface normalTypeface;\n    Typeface selectedTypeface;\n    float typefaceUpdateAreaPercent;\n    int normalColor;\n    int selectColor;\n    int normalColorAttr;\n    int selectedColorAttr;\n    int normalTabIconWidth = QMUITabIcon.TAB_ICON_INTRINSIC;\n    int normalTabIconHeight = QMUITabIcon.TAB_ICON_INTRINSIC;\n    float selectedTabIconScale = 1f;\n    QMUITabIcon tabIcon = null;\n    boolean skinChangeWithTintColor;\n    boolean skinChangeNormalWithTintColor;\n    boolean skinChangeSelectedWithTintColor;\n    int normalIconAttr;\n    int selectedIconAttr;\n    int contentWidth = 0;\n    int contentLeft = 0;\n    @IconPosition int iconPosition = ICON_POSITION_TOP;\n    int gravity = Gravity.CENTER;\n    private CharSequence text;\n    private CharSequence description;\n    int signCountDigits = 2;\n    int signCountHorizontalOffset = 0;\n    int signCountVerticalOffset = 0;\n    int signCountVerticalAlign = SIGN_COUNT_VERTICAL_ALIGN_BOTTOM_TO_CONTENT_TOP;\n    int signCount = NO_SIGN_COUNT_AND_RED_POINT;\n\n    float rightSpaceWeight = 0f;\n    float leftSpaceWeight = 0f;\n    int leftAddonMargin = 0;\n    int rightAddonMargin = 0;\n\n\n    QMUITab(CharSequence text) {\n        this(text, text);\n    }\n\n    QMUITab(CharSequence text, CharSequence description) {\n        this.text = text;\n        this.description = description;\n    }\n\n\n    public CharSequence getText() {\n        return text;\n    }\n\n    public void setText(CharSequence text) {\n        this.text = text;\n    }\n\n    public void setDescription(CharSequence description) {\n        this.description = description;\n    }\n\n    public CharSequence getDescription() {\n        return description;\n    }\n\n    public int getIconPosition() {\n        return iconPosition;\n    }\n\n    public void setIconPosition(@IconPosition int iconPosition) {\n        this.iconPosition = iconPosition;\n    }\n\n    public void setSpaceWeight(float leftWeight, float rightWeight) {\n        leftSpaceWeight = leftWeight;\n        rightSpaceWeight = rightWeight;\n    }\n\n    public void setTypefaceUpdateAreaPercent(float typefaceUpdateAreaPercent) {\n        this.typefaceUpdateAreaPercent = typefaceUpdateAreaPercent;\n    }\n\n    public float getTypefaceUpdateAreaPercent() {\n        return typefaceUpdateAreaPercent;\n    }\n\n    public int getGravity() {\n        return gravity;\n    }\n\n    public void setGravity(int gravity) {\n        this.gravity = gravity;\n    }\n\n    public void setSignCount(int signCount) {\n        this.signCount = signCount;\n    }\n\n    public void setRedPoint(){\n        this.signCount =  RED_POINT_SIGN_COUNT;\n    }\n\n    public int getSignCount(){\n        return this.signCount;\n    }\n\n    public boolean isRedPointShowing(){\n        return this.signCount == RED_POINT_SIGN_COUNT;\n    }\n\n    public void clearSignCountOrRedPoint(){\n        this.signCount = NO_SIGN_COUNT_AND_RED_POINT;\n    }\n\n    public int getNormalColor(@NonNull View skinFollowView) {\n        if(normalColorAttr == 0){\n            return normalColor;\n        }\n        return QMUISkinHelper.getSkinColor(skinFollowView, normalColorAttr);\n    }\n\n    public int getSelectColor(@NonNull View skinFollowView) {\n        if(selectedColorAttr == 0){\n            return selectColor;\n        }\n        return QMUISkinHelper.getSkinColor(skinFollowView, selectedColorAttr);\n    }\n\n    public int getNormalColorAttr() {\n        return normalColorAttr;\n    }\n\n    public int getSelectedColorAttr() {\n        return selectedColorAttr;\n    }\n\n    public int getNormalIconAttr() {\n        return normalIconAttr;\n    }\n\n    public int getSelectedIconAttr() {\n        return selectedIconAttr;\n    }\n\n    public int getNormalTextSize() {\n        return normalTextSize;\n    }\n\n    public int getSelectedTextSize() {\n        return selectedTextSize;\n    }\n\n    public QMUITabIcon getTabIcon() {\n        return tabIcon;\n    }\n\n    public Typeface getNormalTypeface() {\n        return normalTypeface;\n    }\n\n    public Typeface getSelectedTypeface() {\n        return selectedTypeface;\n    }\n\n    public int getNormalTabIconWidth() {\n        if (normalTabIconWidth == QMUITabIcon.TAB_ICON_INTRINSIC && tabIcon != null) {\n            return tabIcon.getIntrinsicWidth();\n        }\n        return normalTabIconWidth;\n    }\n\n    public int getNormalTabIconHeight() {\n        if (normalTabIconHeight == QMUITabIcon.TAB_ICON_INTRINSIC && tabIcon != null) {\n            return tabIcon.getIntrinsicWidth();\n        }\n        return normalTabIconHeight;\n    }\n\n    public float getSelectedTabIconScale() {\n        return selectedTabIconScale;\n    }\n\n    public int getIconTextGap() {\n        return iconTextGap;\n    }\n\n    public boolean isAllowIconDrawOutside() {\n        return allowIconDrawOutside;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.widget.QMUIItemViewsAdapter;\n\npublic class QMUITabAdapter extends QMUIItemViewsAdapter<QMUITab, QMUITabView> implements QMUITabView.Callback {\n    private QMUIBasicTabSegment mTabSegment;\n\n    public QMUITabAdapter(QMUIBasicTabSegment tabSegment, ViewGroup parentView) {\n        super(parentView);\n        mTabSegment = tabSegment;\n    }\n\n    @Override\n    protected QMUITabView createView(ViewGroup parentView) {\n        return new QMUITabView(parentView.getContext());\n    }\n\n    @Override\n    protected final void bind(QMUITab item, QMUITabView view, int position) {\n        onBindTab(item, view, position);\n        view.setCallback(this);\n        // reset\n        if (view.getSelectFraction() != 0f || view.isSelected()) {\n            view.setSelected(false);\n            view.setSelectFraction(0f);\n        }\n    }\n\n    @Override\n    protected void onViewRecycled(QMUITabView qmuiTabView) {\n        qmuiTabView.setSelected(false);\n        qmuiTabView.setSelectFraction(0f);\n        qmuiTabView.setCallback(null);\n    }\n\n    protected void onBindTab(QMUITab item, QMUITabView view, int position) {\n        view.bind(item);\n    }\n\n    @Override\n    public void onClick(QMUITabView view) {\n        int index = getViews().indexOf(view);\n        mTabSegment.onClickTab(view, index);\n    }\n\n    @Override\n    public void onDoubleClick(QMUITabView view) {\n        int index = getViews().indexOf(view);\n        mTabSegment.onDoubleClick(index);\n    }\n\n    @Override\n    public void onLongClick(QMUITabView view) {\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabBuilder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.view.Gravity;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\n\n/**\n * use {@link QMUITabSegment#tabBuilder()} to get a instance\n */\npublic class QMUITabBuilder {\n    /**\n     * icon in normal state\n     */\n    private int normalDrawableAttr = 0;\n    private @Nullable Drawable normalDrawable;\n    /**\n     * icon in selected state\n     */\n    private int selectedDrawableAttr = 0;\n    private @Nullable Drawable selectedDrawable;\n    /**\n     * change icon by tint color, if true, selectedDrawable will not work\n     */\n    private boolean dynamicChangeIconColor = false;\n\n    /**\n     * for skin change. if true, then normalDrawableAttr and selectedDrawableAttr will not work.\n     * otherwise, icon will be replaced by normalDrawableAttr and selectedDrawableAttr\n     */\n    private boolean skinChangeWithTintColor = false;\n    private boolean skinChangeNormalWithTintColor = true;\n    private boolean skinChangeSelectedWithTintColor = true;\n\n    /**\n     * text size in normal state\n     */\n    private int normalTextSize;\n    /**\n     * text size in selected state\n     */\n    private int selectTextSize;\n\n    /**\n     * text color(icon color in if dynamicChangeIconColor == true) in  normal state\n     */\n    private int normalColorAttr = R.attr.qmui_skin_support_tab_normal_color;\n    /**\n     * text color(icon color in if dynamicChangeIconColor == true) in  selected state\n     */\n    private int selectedColorAttr = R.attr.qmui_skin_support_tab_selected_color;\n\n    /**\n     * text color with no skin support\n     */\n    private int normalColor = 0;\n\n    /**\n     * text color with no skin support\n     */\n    private int selectColor = 0;\n\n    /**\n     * icon position(left/top/right/bottom)\n     */\n    private @QMUITab.IconPosition int iconPosition = QMUITab.ICON_POSITION_TOP;\n    /**\n     * gravity of text\n     */\n    private int gravity = Gravity.CENTER;\n\n    private CharSequence text;\n    private CharSequence description;\n\n    /**\n     * text typeface in normal state\n     */\n    private Typeface normalTypeface;\n\n    /**\n     * text typeface in selected state\n     */\n    private Typeface selectedTypeface;\n\n    /**\n     * width of tab icon in normal state\n     */\n    private int normalTabIconWidth = QMUITabIcon.TAB_ICON_INTRINSIC;\n    /**\n     * height of tab icon in normal state\n     */\n    int normalTabIconHeight = QMUITabIcon.TAB_ICON_INTRINSIC;\n    /**\n     * scale of tab icon in selected state\n     */\n    float selectedTabIconScale = 1f;\n\n    float typefaceUpdateAreaPercent = 0.25f;\n\n    /**\n     * signCount or redPoint\n     */\n    private int signCount = QMUITab.NO_SIGN_COUNT_AND_RED_POINT;\n\n    /**\n     * max signCount digits, if the number is over the digits, use 'xx+' to present\n     * if signCountDigits == 2 and number is 110, then component will show '99+'\n     */\n    private int signCountDigits = 2;\n    /**\n     * the horizontal offset of signCount(redPoint) view\n     */\n    private int signCountHorizontalOffset;\n    /**\n     * the vertical offset of signCount(redPoint) view\n     */\n    private int signCountVerticalOffset;\n\n    private int signCountVerticalAlign = QMUITab.SIGN_COUNT_VERTICAL_ALIGN_BOTTOM_TO_CONTENT_TOP;\n\n    /**\n     * the gap between icon and text\n     */\n    private int iconTextGap;\n\n    /**\n     * allow icon draw outside of tab view\n     */\n    private boolean allowIconDrawOutside = true;\n\n\n    QMUITabBuilder(Context context) {\n        iconTextGap = QMUIDisplayHelper.dp2px(context, 2);\n        normalTextSize = selectTextSize = QMUIDisplayHelper.dp2px(context, 12);\n        signCountHorizontalOffset = QMUIDisplayHelper.dp2px(context, 3);\n        signCountVerticalOffset = signCountHorizontalOffset;\n    }\n\n    QMUITabBuilder(QMUITabBuilder other) {\n        this.normalDrawableAttr = other.normalDrawableAttr;\n        this.selectedDrawableAttr = other.selectedDrawableAttr;\n        this.normalDrawable = other.normalDrawable;\n        this.selectedDrawable = other.selectedDrawable;\n        this.dynamicChangeIconColor = other.dynamicChangeIconColor;\n        this.normalTextSize = other.normalTextSize;\n        this.selectTextSize = other.selectTextSize;\n        this.normalColorAttr = other.normalColorAttr;\n        this.selectedColorAttr = other.selectedColorAttr;\n        this.iconPosition = other.iconPosition;\n        this.gravity = other.gravity;\n        this.text = other.text;\n        this.description = other.description;\n        this.signCount = other.signCount;\n        this.signCountDigits = other.signCountDigits;\n        this.signCountHorizontalOffset = other.signCountHorizontalOffset;\n        this.signCountVerticalOffset = other.signCountVerticalOffset;\n        this.signCountVerticalAlign = other.signCountVerticalAlign;\n        this.normalTypeface = other.normalTypeface;\n        this.selectedTypeface = other.selectedTypeface;\n        this.normalTabIconWidth = other.normalTabIconWidth;\n        this.normalTabIconHeight = other.normalTabIconHeight;\n        this.selectedTabIconScale = other.selectedTabIconScale;\n        this.iconTextGap = other.iconTextGap;\n        this.allowIconDrawOutside = other.allowIconDrawOutside;\n        this.typefaceUpdateAreaPercent = other.typefaceUpdateAreaPercent;\n        this.skinChangeNormalWithTintColor = other.skinChangeNormalWithTintColor;\n        this.skinChangeSelectedWithTintColor = other.skinChangeSelectedWithTintColor;\n        this.skinChangeWithTintColor = other.skinChangeWithTintColor;\n        this.normalColor = other.normalColor;\n        this.selectColor = other.selectColor;\n    }\n\n    public QMUITabBuilder setAllowIconDrawOutside(boolean allowIconDrawOutside) {\n        this.allowIconDrawOutside = allowIconDrawOutside;\n        return this;\n    }\n\n    public QMUITabBuilder setTypefaceUpdateAreaPercent(float typefaceUpdateAreaPercent) {\n        this.typefaceUpdateAreaPercent = typefaceUpdateAreaPercent;\n        return this;\n    }\n\n    public QMUITabBuilder setNormalDrawable(Drawable normalDrawable) {\n        this.normalDrawable = normalDrawable;\n        return this;\n    }\n\n    public QMUITabBuilder setNormalDrawableAttr(int normalDrawableAttr) {\n        this.normalDrawableAttr = normalDrawableAttr;\n        return this;\n    }\n\n    public QMUITabBuilder setSelectedDrawable(Drawable selectedDrawable) {\n        this.selectedDrawable = selectedDrawable;\n        return this;\n    }\n\n    public QMUITabBuilder setSelectedDrawableAttr(int selectedDrawableAttr) {\n        this.selectedDrawableAttr = selectedDrawableAttr;\n        return this;\n    }\n\n    @Deprecated\n    public QMUITabBuilder skinChangeWithTintColor(boolean skinChangeWithTintColor){\n        this.skinChangeWithTintColor = skinChangeWithTintColor;\n        return this;\n    }\n\n    public QMUITabBuilder skinChangeNormalWithTintColor(boolean skinChangeNormalWithTintColor){\n        this.skinChangeNormalWithTintColor = skinChangeNormalWithTintColor;\n        return this;\n    }\n\n    public QMUITabBuilder skinChangeSelectedWithTintColor(boolean skinChangeSelectedWithTintColor){\n        this.skinChangeSelectedWithTintColor = skinChangeSelectedWithTintColor;\n        return this;\n    }\n\n    public QMUITabBuilder setTextSize(int normalTextSize, int selectedTextSize) {\n        this.normalTextSize = normalTextSize;\n        this.selectTextSize = selectedTextSize;\n        return this;\n    }\n\n    public QMUITabBuilder setTypeface(Typeface normalTypeface, Typeface selectedTypeface) {\n        this.normalTypeface = normalTypeface;\n        this.selectedTypeface = selectedTypeface;\n        return this;\n    }\n\n    public QMUITabBuilder setNormalIconSizeInfo(int normalWidth, int normalHeight) {\n        this.normalTabIconWidth = normalWidth;\n        this.normalTabIconHeight = normalHeight;\n        return this;\n    }\n\n    public QMUITabBuilder setSelectedIconScale(float selectedScale) {\n        this.selectedTabIconScale = selectedScale;\n        return this;\n    }\n\n    public QMUITabBuilder setIconTextGap(int iconTextGap) {\n        this.iconTextGap = iconTextGap;\n        return this;\n    }\n\n    public QMUITabBuilder setSignCount(int signCount) {\n        this.signCount = signCount;\n        return this;\n    }\n\n    public QMUITabBuilder setSignCountMarginInfo(int digit,\n                                                 int horizontalOffset,\n                                                 int verticalOffset){\n        return setSignCountMarginInfo(digit, horizontalOffset,\n                QMUITab.SIGN_COUNT_VERTICAL_ALIGN_BOTTOM_TO_CONTENT_TOP,\n                verticalOffset);\n    }\n\n    public QMUITabBuilder setSignCountMarginInfo(int digit,\n                                                 int horizontalOffset,\n                                                 int verticalAlign,\n                                                 int verticalOffset\n    ) {\n        this.signCountDigits = digit;\n        this.signCountHorizontalOffset = horizontalOffset;\n        this.signCountVerticalOffset = verticalOffset;\n        this.signCountVerticalAlign = verticalAlign;\n        return this;\n    }\n\n    public QMUITabBuilder setColorAttr(int normalColorAttr, int selectedColorAttr) {\n        this.normalColorAttr = normalColorAttr;\n        this.selectedColorAttr = selectedColorAttr;\n        return this;\n    }\n\n    public QMUITabBuilder setNormalColorAttr(int normalColorAttr) {\n        this.normalColorAttr = normalColorAttr;\n        return this;\n    }\n\n    public QMUITabBuilder setSelectedColorAttr(int selectedColorAttr) {\n        this.selectedColorAttr = selectedColorAttr;\n        return this;\n    }\n\n    public QMUITabBuilder setColor(int normalColor, int selectColor){\n        this.normalColorAttr = 0;\n        this.selectedColorAttr = 0;\n        this.normalColor = normalColor;\n        this.selectColor = selectColor;\n        return this;\n    }\n\n    public QMUITabBuilder setNormalColor(int normalColor) {\n        this.normalColorAttr = 0;\n        this.normalColor = normalColor;\n        return this;\n    }\n\n    public QMUITabBuilder setSelectColor(int selectColor) {\n        this.selectedColorAttr = 0;\n        this.selectColor = selectColor;\n        return this;\n    }\n\n    public QMUITabBuilder setDynamicChangeIconColor(boolean dynamicChangeIconColor) {\n        this.dynamicChangeIconColor = dynamicChangeIconColor;\n        return this;\n    }\n\n    public QMUITabBuilder setGravity(int gravity) {\n        this.gravity = gravity;\n        return this;\n    }\n\n    public QMUITabBuilder setIconPosition(@QMUITab.IconPosition int iconPosition) {\n        this.iconPosition = iconPosition;\n        return this;\n    }\n\n    public QMUITabBuilder setText(CharSequence text) {\n        this.text = text;\n        return this;\n    }\n\n    public QMUITabBuilder setDescription(CharSequence description){\n        this.description = description;\n        return this;\n    }\n\n    public QMUITab build(Context context) {\n        QMUITab tab = new QMUITab(text, description == null ? text : description);\n        if(!skinChangeWithTintColor){\n            if(!skinChangeNormalWithTintColor){\n                if(normalDrawableAttr != 0){\n                    normalDrawable = QMUIResHelper.getAttrDrawable(context, normalDrawableAttr);\n                }\n            }\n\n            if(!skinChangeSelectedWithTintColor){\n                if(selectedDrawableAttr != 0){\n                    selectedDrawable = QMUIResHelper.getAttrDrawable(context, selectedDrawableAttr);\n                }\n            }\n        }\n\n        tab.skinChangeWithTintColor = this.skinChangeWithTintColor;\n        tab.skinChangeNormalWithTintColor = this.skinChangeNormalWithTintColor;\n        tab.skinChangeSelectedWithTintColor = this.skinChangeSelectedWithTintColor;\n\n        if (normalDrawable != null) {\n            if (dynamicChangeIconColor || selectedDrawable == null) {\n                tab.tabIcon = new QMUITabIcon(normalDrawable, null, true);\n                // must same\n                tab.skinChangeSelectedWithTintColor = tab.skinChangeNormalWithTintColor;\n            } else {\n                tab.tabIcon = new QMUITabIcon(normalDrawable, selectedDrawable, false);\n            }\n            tab.tabIcon.setBounds(0, 0, normalTabIconWidth, normalTabIconHeight);\n        }\n        tab.normalIconAttr = this.normalDrawableAttr;\n        tab.selectedIconAttr = this.selectedDrawableAttr;\n        tab.normalTabIconWidth = this.normalTabIconWidth;\n        tab.normalTabIconHeight = this.normalTabIconHeight;\n        tab.selectedTabIconScale = this.selectedTabIconScale;\n        tab.gravity = this.gravity;\n        tab.iconPosition = this.iconPosition;\n        tab.normalTextSize = this.normalTextSize;\n        tab.selectedTextSize = this.selectTextSize;\n        tab.normalTypeface = this.normalTypeface;\n        tab.selectedTypeface = this.selectedTypeface;\n        tab.normalColorAttr = this.normalColorAttr;\n        tab.selectedColorAttr = this.selectedColorAttr;\n        tab.normalColor = this.normalColor;\n        tab.selectColor = this.selectColor;\n        tab.signCount = this.signCount;\n        tab.signCountDigits = this.signCountDigits;\n        tab.signCountHorizontalOffset = this.signCountHorizontalOffset;\n        tab.signCountVerticalAlign = this.signCountVerticalAlign;\n        tab.signCountVerticalOffset = this.signCountVerticalOffset;\n        tab.iconTextGap = this.iconTextGap;\n        tab.typefaceUpdateAreaPercent = this.typefaceUpdateAreaPercent;\n        return tab;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabIcon.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.graphics.drawable.DrawableCompat;\n\nimport com.qmuiteam.qmui.util.QMUIColorHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\npublic class QMUITabIcon extends Drawable implements Drawable.Callback {\n\n    public static final int TAB_ICON_INTRINSIC = -1;\n    private @NonNull\n    Drawable mNormalIconDrawable;\n    private @Nullable\n    Drawable mSelectedIconDrawable;\n    private float mCurrentSelectFraction = 0f;\n    private boolean mDynamicChangeIconColor = true;\n\n    public QMUITabIcon(@NonNull Drawable normalIconDrawable, @Nullable Drawable selectedIconDrawable){\n        this(normalIconDrawable, selectedIconDrawable, true);\n    }\n    public QMUITabIcon(@NonNull Drawable normalIconDrawable, @Nullable Drawable selectedIconDrawable, boolean dynamicChangeIconColor) {\n        mNormalIconDrawable = normalIconDrawable.mutate();\n        mNormalIconDrawable.setCallback(this);\n        if (selectedIconDrawable != null) {\n            mSelectedIconDrawable = selectedIconDrawable.mutate();\n            mSelectedIconDrawable.setCallback(this);\n        }\n\n        mNormalIconDrawable.setAlpha(255);\n        int nw = mNormalIconDrawable.getIntrinsicWidth();\n        int nh = mNormalIconDrawable.getIntrinsicHeight();\n        mNormalIconDrawable.setBounds(0, 0, nw, nh);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.setAlpha(0);\n            mSelectedIconDrawable.setBounds(0, 0, nw, nh);\n        }\n        mDynamicChangeIconColor = dynamicChangeIconColor;\n    }\n\n    public boolean hasSelectedIcon() {\n        return mSelectedIconDrawable != null;\n    }\n\n    public void tint(int normalColor, int selectColor) {\n        if (mSelectedIconDrawable == null) {\n            DrawableCompat.setTint(mNormalIconDrawable, QMUIColorHelper.computeColor(normalColor, selectColor, mCurrentSelectFraction));\n        } else {\n            DrawableCompat.setTint(mNormalIconDrawable, normalColor);\n            DrawableCompat.setTint(mSelectedIconDrawable, selectColor);\n        }\n        invalidateSelf();\n    }\n\n    public void tintNormal(int normalColor){\n        DrawableCompat.setTint(mNormalIconDrawable, normalColor);\n        invalidateSelf();\n    }\n\n    public void tintSelected(int selectColor){\n        if (mSelectedIconDrawable != null) {\n            DrawableCompat.setTint(mSelectedIconDrawable, selectColor);\n            invalidateSelf();\n        }\n    }\n\n    public void srcNormal(@NonNull Drawable normalDrawable){\n        int normalAlpha = (int) (255 * (1 - mCurrentSelectFraction));\n        mNormalIconDrawable.setCallback(null);\n        mNormalIconDrawable = normalDrawable.mutate();\n        mNormalIconDrawable.setCallback(this);\n        mNormalIconDrawable.setAlpha(normalAlpha);\n        invalidateSelf();\n    }\n\n    public void srcSelected(@NonNull Drawable selectDrawable){\n        int selectedAlpha = (int) (255 * mCurrentSelectFraction);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.setCallback(null);\n        }\n        mSelectedIconDrawable = selectDrawable.mutate();\n        mSelectedIconDrawable.setCallback(this);\n        mSelectedIconDrawable.setAlpha(selectedAlpha);\n        invalidateSelf();\n    }\n\n\n    public void src(@NonNull Drawable normalDrawable, @NonNull Drawable selectDrawable) {\n        int normalAlpha = (int) (255 * (1 - mCurrentSelectFraction));\n        mNormalIconDrawable.setCallback(null);\n        mNormalIconDrawable = normalDrawable.mutate();\n        mNormalIconDrawable.setCallback(this);\n        mNormalIconDrawable.setAlpha(normalAlpha);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.setCallback(null);\n        }\n        mSelectedIconDrawable = selectDrawable.mutate();\n        mSelectedIconDrawable.setCallback(this);\n        mSelectedIconDrawable.setAlpha(255 - normalAlpha);\n        invalidateSelf();\n    }\n\n    public void src(@NonNull Drawable normalDrawable, int normalColor, int selectColor) {\n        mNormalIconDrawable.setCallback(this);\n        mNormalIconDrawable = normalDrawable.mutate();\n        mNormalIconDrawable.setCallback(this);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.setCallback(null);\n            mSelectedIconDrawable = null;\n        }\n        if(mDynamicChangeIconColor){\n            DrawableCompat.setTint(mNormalIconDrawable, QMUIColorHelper.computeColor(normalColor, selectColor, mCurrentSelectFraction));\n        }\n\n        invalidateSelf();\n    }\n\n    @Override\n    public int getIntrinsicWidth() {\n        return mNormalIconDrawable.getIntrinsicWidth();\n    }\n\n    @Override\n    public int getIntrinsicHeight() {\n        return mNormalIconDrawable.getIntrinsicHeight();\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n        // not used\n    }\n\n    @Override\n    public void setColorFilter(@Nullable ColorFilter colorFilter) {\n        // not used\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.TRANSLUCENT;\n    }\n\n    /**\n     * set the select faction for QMUITabIcon, value must be in [0, 1]\n     *\n     * @param fraction muse be in [0, 1]\n     */\n    public void setSelectFraction(float fraction, int color) {\n        fraction = QMUILangHelper.constrain(fraction, 0f, 1f);\n        mCurrentSelectFraction = fraction;\n        if (mSelectedIconDrawable == null) {\n            if(mDynamicChangeIconColor){\n                DrawableCompat.setTint(mNormalIconDrawable, color);\n            }\n        } else {\n            int normalAlpha = (int) (255 * (1 - fraction));\n            mNormalIconDrawable.setAlpha(normalAlpha);\n            mSelectedIconDrawable.setAlpha(255 - normalAlpha);\n        }\n        invalidateSelf();\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas) {\n        mNormalIconDrawable.draw(canvas);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.draw(canvas);\n        }\n    }\n\n    @Override\n    protected void onBoundsChange(Rect bounds) {\n        mNormalIconDrawable.setBounds(bounds);\n        if (mSelectedIconDrawable != null) {\n            mSelectedIconDrawable.setBounds(bounds);\n        }\n    }\n\n    @Override\n    public void invalidateDrawable(@NonNull Drawable who) {\n        Callback callback = getCallback();\n        if(callback != null){\n            callback.invalidateDrawable(who);\n        }\n    }\n\n    @Override\n    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {\n        Callback callback = getCallback();\n        if(callback != null){\n            callback.scheduleDrawable(who, what, when);\n        }\n    }\n\n    @Override\n    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {\n        Callback callback = getCallback();\n        if(callback != null){\n            callback.unscheduleDrawable(who, what);\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabIndicator.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIDrawableHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.graphics.drawable.DrawableCompat;\n\npublic class QMUITabIndicator {\n\n    /**\n     * the height of indicator\n     */\n    private int mIndicatorHeight;\n    /**\n     * is indicator layout in top of QMUITabSegment?\n     */\n    private boolean mIndicatorTop = false;\n    /**\n     * use a drawable to present the indicator\n     */\n    private @Nullable Drawable mIndicatorDrawable;\n    /**\n     * the width of indicator changed when toggle to different tab\n     */\n    private boolean mIsIndicatorWidthFollowContent = true;\n\n    /**\n     * indicator rect, draw directly\n     */\n    private Rect mIndicatorRect = null;\n\n    /**\n     * indicator paint, draw directly\n     */\n    private Paint mIndicatorPaint = null;\n\n    private int mFixedColorAttr = 0;\n    private boolean mShouldReGetFixedColor = true;\n    private int mFixedColor = 0;\n\n    public QMUITabIndicator(int indicatorHeight, boolean indicatorTop,\n                            boolean isIndicatorWidthFollowContent){\n        this(indicatorHeight, indicatorTop, isIndicatorWidthFollowContent, 0);\n    }\n\n    public QMUITabIndicator(int indicatorHeight, boolean indicatorTop,\n                            boolean isIndicatorWidthFollowContent,  int fixedColorAttr) {\n        mIndicatorHeight = indicatorHeight;\n        mIndicatorTop = indicatorTop;\n        mIsIndicatorWidthFollowContent = isIndicatorWidthFollowContent;\n        mFixedColorAttr = fixedColorAttr;\n    }\n\n    public QMUITabIndicator(@NonNull Drawable drawable, boolean indicatorTop,\n                            boolean isIndicatorWidthFollowContent){\n        this(drawable, indicatorTop, isIndicatorWidthFollowContent, 0);\n    }\n\n    public QMUITabIndicator(@NonNull Drawable drawable, boolean indicatorTop,\n                            boolean isIndicatorWidthFollowContent, int fixedColorAttr) {\n        mIndicatorDrawable = drawable;\n        mIndicatorHeight = drawable.getIntrinsicHeight();\n        mIndicatorTop = indicatorTop;\n        mIsIndicatorWidthFollowContent = isIndicatorWidthFollowContent;\n        mFixedColorAttr = fixedColorAttr;\n    }\n\n    public boolean isIndicatorWidthFollowContent() {\n        return mIsIndicatorWidthFollowContent;\n    }\n\n    public boolean isIndicatorTop() {\n        return mIndicatorTop;\n    }\n\n    @Deprecated\n    protected void updateInfo(int left, int width, int color){\n        if (mIndicatorRect == null) {\n            mIndicatorRect = new Rect(left, 0,\n                    left + width, 0);\n        } else {\n            mIndicatorRect.left = left;\n            mIndicatorRect.right = left + width;\n        }\n        if(mFixedColorAttr == 0){\n            updateColor(color);\n        }\n    }\n\n    protected void updateInfo(int left, int width, int color, float offsetPercent) {\n        updateInfo(left, width, color);\n    }\n\n    protected void updateColor(int color){\n        if (mIndicatorDrawable != null) {\n            DrawableCompat.setTint(mIndicatorDrawable, color);\n        } else {\n            if (mIndicatorPaint == null) {\n                mIndicatorPaint = new Paint();\n                mIndicatorPaint.setStyle(Paint.Style.FILL);\n            }\n            mIndicatorPaint.setColor(color);\n        }\n    }\n\n    protected void draw(@NonNull View hostView, @NonNull Canvas canvas, int viewTop, int viewBottom) {\n        if (mIndicatorRect != null) {\n            if(mFixedColorAttr != 0 && mShouldReGetFixedColor){\n                mShouldReGetFixedColor = false;\n                mFixedColor = QMUISkinHelper.getSkinColor(hostView, mFixedColorAttr);\n                updateColor(mFixedColor);\n            }\n            if (mIndicatorTop) {\n                mIndicatorRect.top = viewTop;\n                mIndicatorRect.bottom = mIndicatorRect.top + mIndicatorHeight;\n            } else {\n                mIndicatorRect.bottom = viewBottom;\n                mIndicatorRect.top = mIndicatorRect.bottom - mIndicatorHeight;\n            }\n            if (mIndicatorDrawable != null) {\n                mIndicatorDrawable.setBounds(mIndicatorRect);\n                mIndicatorDrawable.draw(canvas);\n            } else {\n                canvas.drawRect(mIndicatorRect, mIndicatorPaint);\n            }\n        }\n    }\n\n    protected void handleSkinChange(@NonNull QMUISkinManager manager, int skinIndex,\n                                    @NonNull Resources.Theme theme,\n                                    @Nullable QMUITab selectedTab){\n        mShouldReGetFixedColor = true;\n        if(selectedTab != null && mFixedColorAttr == 0){\n            updateColor(\n                    selectedTab.selectedColorAttr == 0 ? selectedTab.selectColor : QMUIResHelper.getAttrColor(theme,selectedTab.selectedColorAttr));\n        }\n    }\n}"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabSegment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.content.Context;\nimport android.database.DataSetObserver;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\n\nimport java.lang.ref.WeakReference;\n\n\n/**\n * 在 {@link QMUIBasicTabSegment} 的基础上添加与 {@link ViewPager} 的联动使用\n */\npublic class QMUITabSegment extends QMUIBasicTabSegment {\n\n    private static final String TAG = \"QMUITabSegment\";\n\n    /**\n     * the scrollState of ViewPager\n     */\n    private int mViewPagerScrollState = ViewPager.SCROLL_STATE_IDLE;\n\n\n    private ViewPager mViewPager;\n    private PagerAdapter mPagerAdapter;\n    private DataSetObserver mPagerAdapterObserver;\n    private ViewPager.OnPageChangeListener mOnPageChangeListener;\n    private OnTabSelectedListener mViewPagerSelectedListener;\n    private AdapterChangeListener mAdapterChangeListener;\n\n\n    public QMUITabSegment(Context context) {\n        super(context);\n    }\n\n    public QMUITabSegment(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUITabSegment(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected boolean needPreventEvent() {\n        return mViewPagerScrollState != ViewPager.SCROLL_STATE_IDLE;\n    }\n\n    @Override\n    public void notifyDataChanged() {\n        super.notifyDataChanged();\n        populateFromPagerAdapter(false);\n    }\n\n    public void notifyDataRefreshed(){\n        super.notifyDataChanged();\n    }\n\n    public void setupWithViewPager(@Nullable ViewPager viewPager) {\n        setupWithViewPager(viewPager, true);\n    }\n\n    public void setupWithViewPager(@Nullable ViewPager viewPager, boolean useAdapterTitle) {\n        setupWithViewPager(viewPager, useAdapterTitle, true);\n    }\n\n    /**\n     * associate QMUITabSegment with a {@link ViewPager}\n     *\n     * @param viewPager       the ViewPager to associate\n     * @param useAdapterTitle populate the tab with viewPager.adapter.getTitle\n     * @param autoRefresh     refresh QMUITabSegment when viewPager.adapter changed.\n     */\n    public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean useAdapterTitle, boolean autoRefresh) {\n        if (mViewPager != null) {\n            // If we've already been setup with a ViewPager, remove us from it\n            if (mOnPageChangeListener != null) {\n                mViewPager.removeOnPageChangeListener(mOnPageChangeListener);\n            }\n\n            if (mAdapterChangeListener != null) {\n                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);\n            }\n        }\n\n        if (mViewPagerSelectedListener != null) {\n            // If we already have a tab selected listener for the ViewPager, remove it\n            removeOnTabSelectedListener(mViewPagerSelectedListener);\n            mViewPagerSelectedListener = null;\n        }\n\n        if (viewPager != null) {\n            mViewPager = viewPager;\n\n            // Add our custom OnPageChangeListener to the ViewPager\n            if (mOnPageChangeListener == null) {\n                mOnPageChangeListener = new TabLayoutOnPageChangeListener(this);\n            }\n            viewPager.addOnPageChangeListener(mOnPageChangeListener);\n\n            // Now we'll add a tab selected listener to set ViewPager's current item\n            mViewPagerSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);\n            addOnTabSelectedListener(mViewPagerSelectedListener);\n\n            final PagerAdapter adapter = viewPager.getAdapter();\n            if (adapter != null) {\n                // Now we'll populate ourselves from the pager adapter, adding an observer if\n                // autoRefresh is enabled\n                setPagerAdapter(adapter, useAdapterTitle, autoRefresh);\n            }\n\n            // Add a listener so that we're notified of any adapter changes\n            if (mAdapterChangeListener == null) {\n                mAdapterChangeListener = new AdapterChangeListener(useAdapterTitle);\n            }\n            mAdapterChangeListener.setAutoRefresh(autoRefresh);\n            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);\n        } else {\n            // We've been given a null ViewPager so we need to clear out the internal state,\n            // listeners and observers\n            mViewPager = null;\n            setPagerAdapter(null, false, false);\n        }\n    }\n\n\n    private void setViewPagerScrollState(int state) {\n        mViewPagerScrollState = state;\n        if (mViewPagerScrollState == ViewPager.SCROLL_STATE_IDLE) {\n            if (mPendingSelectedIndex != NO_POSITION && mSelectAnimator == null) {\n                selectTab(mPendingSelectedIndex, true, false);\n                mPendingSelectedIndex = NO_POSITION;\n            }\n        }\n    }\n\n\n    void populateFromPagerAdapter(boolean useAdapterTitle) {\n        if (mPagerAdapter == null) {\n            if (useAdapterTitle) {\n                reset();\n            }\n            return;\n        }\n        final int adapterCount = mPagerAdapter.getCount();\n        if (useAdapterTitle) {\n            reset();\n            for (int i = 0; i < adapterCount; i++) {\n                addTab(mTabBuilder.setText(mPagerAdapter.getPageTitle(i)).build(getContext()));\n            }\n            super.notifyDataChanged();\n        }\n\n        if (mViewPager != null && adapterCount > 0) {\n            final int curItem = mViewPager.getCurrentItem();\n            selectTab(curItem, true, false);\n        }\n    }\n\n\n    void setPagerAdapter(@Nullable final PagerAdapter adapter, boolean useAdapterTitle, final boolean addObserver) {\n        if (mPagerAdapter != null && mPagerAdapterObserver != null) {\n            // If we already have a PagerAdapter, unregister our observer\n            mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);\n        }\n\n        mPagerAdapter = adapter;\n\n        if (addObserver && adapter != null) {\n            // Register our observer on the new adapter\n            if (mPagerAdapterObserver == null) {\n                mPagerAdapterObserver = new PagerAdapterObserver(useAdapterTitle);\n            }\n            adapter.registerDataSetObserver(mPagerAdapterObserver);\n        }\n\n        // Finally make sure we reflect the new adapter\n        populateFromPagerAdapter(useAdapterTitle);\n    }\n\n    public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {\n        private final WeakReference<QMUITabSegment> mTabSegmentRef;\n\n        public TabLayoutOnPageChangeListener(QMUITabSegment tabSegment) {\n            mTabSegmentRef = new WeakReference<>(tabSegment);\n        }\n\n        @Override\n        public void onPageScrollStateChanged(final int state) {\n            final QMUITabSegment tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null) {\n                tabSegment.setViewPagerScrollState(state);\n            }\n\n        }\n\n        @Override\n        public void onPageScrolled(final int position, final float positionOffset,\n                                   final int positionOffsetPixels) {\n            final QMUITabSegment tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null) {\n                tabSegment.updateIndicatorPosition(position, positionOffset);\n            }\n        }\n\n        @Override\n        public void onPageSelected(final int position) {\n            final QMUITabSegment tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null && tabSegment.mPendingSelectedIndex != NO_POSITION) {\n                tabSegment.mPendingSelectedIndex = position;\n                return;\n            }\n            if (tabSegment != null && tabSegment.getSelectedIndex() != position\n                    && position < tabSegment.getTabCount()) {\n                tabSegment.selectTab(position, true, false);\n            }\n        }\n    }\n\n    private static class ViewPagerOnTabSelectedListener implements OnTabSelectedListener {\n        private final ViewPager mViewPager;\n\n        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {\n            mViewPager = viewPager;\n        }\n\n        @Override\n        public void onTabSelected(int index) {\n            mViewPager.setCurrentItem(index, false);\n        }\n\n        @Override\n        public void onTabUnselected(int index) {\n        }\n\n        @Override\n        public void onTabReselected(int index) {\n        }\n\n        @Override\n        public void onDoubleTap(int index) {\n\n        }\n    }\n\n    private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener {\n        private boolean mAutoRefresh;\n        private final boolean mUseAdapterTitle;\n\n        AdapterChangeListener(boolean useAdapterTitle) {\n            mUseAdapterTitle = useAdapterTitle;\n        }\n\n        @Override\n        public void onAdapterChanged(@NonNull ViewPager viewPager,\n                                     @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) {\n            if (mViewPager == viewPager) {\n                setPagerAdapter(newAdapter, mUseAdapterTitle, mAutoRefresh);\n            }\n        }\n\n        void setAutoRefresh(boolean autoRefresh) {\n            mAutoRefresh = autoRefresh;\n        }\n    }\n\n\n    private class PagerAdapterObserver extends DataSetObserver {\n        private final boolean mUseAdapterTitle;\n\n        PagerAdapterObserver(boolean useAdapterTitle) {\n            mUseAdapterTitle = useAdapterTitle;\n        }\n\n        @Override\n        public void onChanged() {\n            populateFromPagerAdapter(mUseAdapterTitle);\n        }\n\n        @Override\n        public void onInvalidated() {\n            populateFromPagerAdapter(mUseAdapterTitle);\n        }\n    }\n\n    /**\n     * Please use QMUIBasicTabSegment.OnTabClickListener for a replacement\n     */\n    @Deprecated\n    public interface OnTabClickListener extends QMUIBasicTabSegment.OnTabClickListener{\n\n    }\n\n    /**\n     * Please use QMUIBasicTabSegment.OnTabSelectedListener for a replacement\n     */\n    @Deprecated\n    public interface OnTabSelectedListener extends QMUIBasicTabSegment.OnTabSelectedListener{\n\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabSegment2.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.Nullable;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport java.lang.ref.WeakReference;\n\n\n/**\n * 在 {@link QMUIBasicTabSegment} 的基础上添加与 {@link ViewPager2} 的联动使用\n */\npublic class QMUITabSegment2 extends QMUIBasicTabSegment {\n\n    private static final String TAG = \"QMUITabSegment\";\n\n    /**\n     * the scrollState of ViewPager\n     */\n    private int mViewPagerScrollState = ViewPager2.SCROLL_STATE_IDLE;\n\n\n    private ViewPager2 mViewPager;\n    private ViewPager2.OnPageChangeCallback mOnPageChangeListener;\n    private OnTabSelectedListener mViewPagerSelectedListener;\n\n\n    public QMUITabSegment2(Context context) {\n        super(context);\n    }\n\n    public QMUITabSegment2(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QMUITabSegment2(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected boolean needPreventEvent() {\n        return mViewPagerScrollState != ViewPager2.SCROLL_STATE_IDLE;\n    }\n\n\n    /**\n     * associate QMUITabSegment2 with a {@link ViewPager2}\n     *\n     * @param viewPager the ViewPager2 to associate\n     */\n    public void setupWithViewPager(@Nullable final ViewPager2 viewPager) {\n        if (mViewPager != null) {\n            if (mOnPageChangeListener != null) {\n                mViewPager.unregisterOnPageChangeCallback(mOnPageChangeListener);\n            }\n        }\n\n        if (mViewPagerSelectedListener != null) {\n            removeOnTabSelectedListener(mViewPagerSelectedListener);\n            mViewPagerSelectedListener = null;\n        }\n\n        if (viewPager != null) {\n            mViewPager = viewPager;\n            if (mOnPageChangeListener == null) {\n                mOnPageChangeListener = new TabLayoutOnPageChangeListener(this);\n            }\n            viewPager.registerOnPageChangeCallback(mOnPageChangeListener);\n\n            mViewPagerSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);\n            addOnTabSelectedListener(mViewPagerSelectedListener);\n\n            final int curItem = mViewPager.getCurrentItem();\n            selectTab(curItem, true, false);\n        } else {\n            mViewPager = null;\n        }\n    }\n\n\n    private void setViewPagerScrollState(int state) {\n        mViewPagerScrollState = state;\n        if (mViewPagerScrollState == ViewPager2.SCROLL_STATE_IDLE) {\n            if (mPendingSelectedIndex != NO_POSITION && mSelectAnimator == null) {\n                selectTab(mPendingSelectedIndex, true, false);\n                mPendingSelectedIndex = NO_POSITION;\n            }\n        }\n    }\n\n\n    public static class TabLayoutOnPageChangeListener extends ViewPager2.OnPageChangeCallback {\n        private final WeakReference<QMUITabSegment2> mTabSegmentRef;\n\n        public TabLayoutOnPageChangeListener(QMUITabSegment2 tabSegment) {\n            mTabSegmentRef = new WeakReference<>(tabSegment);\n        }\n\n        @Override\n        public void onPageScrollStateChanged(final int state) {\n            final QMUITabSegment2 tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null) {\n                tabSegment.setViewPagerScrollState(state);\n            }\n\n        }\n\n        @Override\n        public void onPageScrolled(final int position, final float positionOffset,\n                                   final int positionOffsetPixels) {\n            final QMUITabSegment2 tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null) {\n                tabSegment.updateIndicatorPosition(position, positionOffset);\n            }\n        }\n\n        @Override\n        public void onPageSelected(final int position) {\n            final QMUITabSegment2 tabSegment = mTabSegmentRef.get();\n            if (tabSegment != null && tabSegment.mPendingSelectedIndex != NO_POSITION) {\n                tabSegment.mPendingSelectedIndex = position;\n                return;\n            }\n            if (tabSegment != null && tabSegment.getSelectedIndex() != position\n                    && position < tabSegment.getTabCount()) {\n                tabSegment.selectTab(position, true, false);\n            }\n        }\n    }\n\n    private static class ViewPagerOnTabSelectedListener implements OnTabSelectedListener {\n        private final ViewPager2 mViewPager;\n\n        public ViewPagerOnTabSelectedListener(ViewPager2 viewPager) {\n            mViewPager = viewPager;\n        }\n\n        @Override\n        public void onTabSelected(int index) {\n            mViewPager.setCurrentItem(index, false);\n        }\n\n        @Override\n        public void onTabUnselected(int index) {\n        }\n\n        @Override\n        public void onTabReselected(int index) {\n        }\n\n        @Override\n        public void onDoubleTap(int index) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/tab/QMUITabView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.tab;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Point;\nimport android.graphics.drawable.Drawable;\nimport android.view.GestureDetector;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.accessibility.AccessibilityNodeInfo;\nimport android.view.animation.Interpolator;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.collection.SimpleArrayMap;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerView;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.defaultAttr.QMUISkinSimpleDefaultAttrProvider;\nimport com.qmuiteam.qmui.util.QMUICollapsingTextHelper;\nimport com.qmuiteam.qmui.util.QMUIColorHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class QMUITabView extends FrameLayout implements IQMUISkinHandlerView {\n    private static final String TAG = \"QMUITabView\";\n    private QMUITab mTab;\n    private QMUICollapsingTextHelper mCollapsingTextHelper;\n    private Interpolator mPositionInterpolator;\n    private GestureDetector mGestureDetector;\n    private Callback mCallback;\n    private float mCurrentIconLeft = 0;\n    private float mCurrentIconTop = 0;\n    private float mCurrentTextLeft = 0;\n    private float mCurrentTextTop = 0;\n    private float mCurrentIconWidth = 0;\n    private float mCurrentIconHeight = 0;\n    private float mCurrentTextWidth = 0;\n    private float mCurrentTextHeight = 0;\n\n    private float mNormalIconLeft = 0;\n    private float mNormalIconTop = 0;\n    private float mNormalTextLeft = 0;\n    private float mNormalTextTop = 0;\n    private float mSelectedIconLeft = 0;\n    private float mSelectedIconTop = 0;\n    private float mSelectedTextLeft = 0;\n    private float mSelectedTextTop = 0;\n\n    private float mSelectFraction = 0f;\n\n    private QMUIRoundButton mSignCountView;\n\n    public QMUITabView(@NonNull Context context) {\n        super(context);\n        \n        // 使得每个tab可被诸如TalkBack等屏幕阅读器聚焦\n        // 这样视力受损用户（如盲人、低、弱视力）就能与tab交互\n        this.setFocusable(true);\n        this.setFocusableInTouchMode(true);\n        \n        setWillNotDraw(false);\n        mCollapsingTextHelper = new QMUICollapsingTextHelper(this, 1f);\n        mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {\n\n            @Override\n            public boolean onDoubleTap(MotionEvent e) {\n                if (mCallback != null) {\n                    mCallback.onDoubleClick(QMUITabView.this);\n                    return true;\n                }\n                return false;\n            }\n\n            @Override\n            public boolean onSingleTapUp(MotionEvent e) {\n                if (mCallback != null) {\n                    mCallback.onClick(QMUITabView.this);\n                    return false;\n                }\n                return false;\n            }\n\n            @Override\n            public boolean onDown(MotionEvent e) {\n                return mCallback != null;\n            }\n\n            @Override\n            public void onLongPress(MotionEvent e) {\n                if (mCallback != null) {\n                    mCallback.onLongClick(QMUITabView.this);\n                }\n            }\n        });\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    public void setPositionInterpolator(Interpolator positionInterpolator) {\n        mPositionInterpolator = positionInterpolator;\n        mCollapsingTextHelper.setPositionInterpolator(positionInterpolator);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);\n    }\n\n    public void bind(QMUITab tab) {\n        mCollapsingTextHelper.setTextSize(tab.normalTextSize, tab.selectedTextSize, false);\n        mCollapsingTextHelper.setTypeface(tab.normalTypeface, tab.selectedTypeface, false);\n        mCollapsingTextHelper.setTypefaceUpdateAreaPercent(tab.typefaceUpdateAreaPercent);\n        int gravity = Gravity.LEFT | Gravity.TOP;\n        mCollapsingTextHelper.setGravity(gravity, gravity, false);\n        mCollapsingTextHelper.setText(tab.getText());\n        mTab = tab;\n        if(tab.tabIcon != null){\n            tab.tabIcon.setCallback(this);\n        }\n        boolean hasRedPoint = mTab.signCount == QMUITab.RED_POINT_SIGN_COUNT;\n        boolean hasSignCount = mTab.signCount > 0;\n        if (hasRedPoint || hasSignCount) {\n            ensureSignCountView(getContext());\n\n            FrameLayout.LayoutParams signCountLp = (FrameLayout.LayoutParams) mSignCountView.getLayoutParams();\n            if (hasSignCount) {\n                mSignCountView.setText(\n                        QMUILangHelper.formatNumberToLimitedDigits(mTab.signCount, mTab.signCountDigits));\n                mSignCountView.setMinWidth(QMUIResHelper.getAttrDimen(getContext(),\n                        R.attr.qmui_tab_sign_count_view_min_size_with_text));\n                signCountLp.width = ViewGroup.LayoutParams.WRAP_CONTENT;\n                signCountLp.height = QMUIResHelper.getAttrDimen(getContext(),\n                        R.attr.qmui_tab_sign_count_view_min_size_with_text);\n            } else {\n                mSignCountView.setText(null);\n                int redPointSize = QMUIResHelper.getAttrDimen(getContext(),\n                        R.attr.qmui_tab_sign_count_view_min_size);\n                signCountLp.width = redPointSize;\n                signCountLp.height = redPointSize;\n            }\n            mSignCountView.setLayoutParams(signCountLp);\n            mSignCountView.setVisibility(View.VISIBLE);\n        } else {\n            if (mSignCountView != null) {\n                mSignCountView.setVisibility(View.GONE);\n            }\n        }\n        updateSkinInfo(tab);\n        requestLayout();\n        setContentDescription(tab.getDescription());\n    }\n\n\n    public float getSelectFraction() {\n        return mSelectFraction;\n    }\n\n    public void setSelectFraction(float fraction) {\n        fraction = QMUILangHelper.constrain(fraction, 0f, 1f);\n        mSelectFraction = fraction;\n        QMUITabIcon tabIcon = mTab.getTabIcon();\n        if (tabIcon != null) {\n            tabIcon.setSelectFraction(fraction,\n                    QMUIColorHelper.computeColor(mTab.getNormalColor(this),\n                            mTab.getSelectColor(this), fraction));\n        }\n        updateCurrentInfo(fraction);\n        mCollapsingTextHelper.setExpansionFraction(1 - fraction);\n        if (mSignCountView != null) {\n            Point point = calculateSignCountLayoutPosition();\n            int x = point.x, y = point.y;\n            if (point.x + mSignCountView.getMeasuredWidth() > getMeasuredWidth()) {\n                x = getMeasuredWidth() - mSignCountView.getMeasuredWidth();\n            }\n\n            if (point.y - mSignCountView.getMeasuredHeight() < 0) {\n                y = mSignCountView.getMeasuredHeight();\n            }\n            ViewCompat.offsetLeftAndRight(mSignCountView, x - mSignCountView.getLeft());\n            ViewCompat.offsetTopAndBottom(mSignCountView, y - mSignCountView.getBottom());\n        }\n    }\n\n    private void updateCurrentInfo(float fraction) {\n        mCurrentIconLeft = QMUICollapsingTextHelper.lerp(\n                mNormalIconLeft, mSelectedIconLeft, fraction, mPositionInterpolator);\n        mCurrentIconTop = QMUICollapsingTextHelper.lerp(\n                mNormalIconTop, mSelectedIconTop, fraction, mPositionInterpolator);\n        int normalIconWidth = mTab.getNormalTabIconWidth();\n        int normalIconHeight = mTab.getNormalTabIconHeight();\n        float selectedScale = mTab.getSelectedTabIconScale();\n        mCurrentIconWidth = QMUICollapsingTextHelper.lerp(normalIconWidth,\n                normalIconWidth * selectedScale, fraction, mPositionInterpolator);\n        mCurrentIconHeight = QMUICollapsingTextHelper.lerp(normalIconHeight,\n                normalIconHeight * selectedScale, fraction, mPositionInterpolator);\n\n        mCurrentTextLeft = QMUICollapsingTextHelper.lerp(\n                mNormalTextLeft, mSelectedTextLeft, fraction, mPositionInterpolator);\n        mCurrentTextTop = QMUICollapsingTextHelper.lerp(\n                mNormalTextTop, mSelectedTextTop, fraction, mPositionInterpolator);\n\n        float normalTextWidth = mCollapsingTextHelper.getCollapsedTextWidth();\n        float normalTextHeight = mCollapsingTextHelper.getCollapsedTextHeight();\n        float selectedTextWidth = mCollapsingTextHelper.getExpandedTextWidth();\n        float selectedTextHeight = mCollapsingTextHelper.getExpandedTextHeight();\n        mCurrentTextWidth = QMUICollapsingTextHelper.lerp(\n                normalTextWidth, selectedTextWidth, fraction, mPositionInterpolator);\n        mCurrentTextHeight = QMUICollapsingTextHelper.lerp(\n                normalTextHeight, selectedTextHeight, fraction, mPositionInterpolator);\n    }\n\n    public int getContentViewWidth() {\n        if (mTab == null) {\n            return 0;\n        }\n        float textWidth = mCollapsingTextHelper.getExpandedTextWidth();\n        if (mTab.getTabIcon() == null) {\n            return (int) (textWidth + 0.5);\n        }\n        int iconPosition = mTab.getIconPosition();\n        float iconWidth = mTab.getNormalTabIconWidth() * mTab.getSelectedTabIconScale();\n        if (iconPosition == QMUITab.ICON_POSITION_BOTTOM || iconPosition == QMUITab.ICON_POSITION_TOP) {\n            return (int) (Math.max(iconWidth, textWidth) + 0.5);\n        }\n        return (int) (iconWidth + textWidth + mTab.getIconTextGap() + 0.5);\n    }\n\n    public int getContentViewLeft() {\n        if (mTab == null) {\n            return 0;\n        }\n        if (mTab.getTabIcon() == null) {\n            return (int) (mSelectedTextLeft + 0.5);\n        }\n        int iconPosition = mTab.getIconPosition();\n        if (iconPosition == QMUITab.ICON_POSITION_BOTTOM || iconPosition == QMUITab.ICON_POSITION_TOP) {\n            return (int) Math.min(mSelectedTextLeft, mSelectedIconLeft + 0.5);\n        } else if (iconPosition == QMUITab.ICON_POSITION_LEFT) {\n            return (int) (mSelectedIconLeft + 0.5);\n        } else {\n            return (int) (mSelectedTextLeft + 0.5);\n        }\n    }\n\n    private QMUIRoundButton ensureSignCountView(Context context) {\n        if (mSignCountView == null) {\n            mSignCountView = createSignCountView(context);\n            FrameLayout.LayoutParams signCountLp;\n            if (mSignCountView.getLayoutParams() != null) {\n                signCountLp = new FrameLayout.LayoutParams(mSignCountView.getLayoutParams());\n            } else {\n                signCountLp = new FrameLayout.LayoutParams(\n                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n            addView(mSignCountView, signCountLp);\n        }\n        return mSignCountView;\n    }\n\n    protected QMUIRoundButton createSignCountView(Context context) {\n        QMUIRoundButton btn = new QMUIRoundButton(\n                context, null, R.attr.qmui_tab_sign_count_view);\n        QMUISkinSimpleDefaultAttrProvider skinProvider = new QMUISkinSimpleDefaultAttrProvider();\n        skinProvider.setDefaultSkinAttr(\n                QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_tab_sign_count_view_bg_color);\n        skinProvider.setDefaultSkinAttr(\n                QMUISkinValueBuilder.TEXT_COLOR, R.attr.qmui_skin_support_tab_sign_count_view_text_color);\n        btn.setTag(R.id.qmui_skin_default_attr_provider, skinProvider);\n        return btn;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        if (mTab == null) {\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n            return;\n        }\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        onMeasureTab(widthSize, heightSize);\n        int useWidthMeasureSpec = widthMeasureSpec;\n        int useHeightMeasureSpec = heightMeasureSpec;\n        QMUITabIcon icon = mTab.getTabIcon();\n        int iconPosition = mTab.getIconPosition();\n        if (widthMode == MeasureSpec.AT_MOST) {\n            if (icon == null) {\n                widthSize = (int) mCollapsingTextHelper.getExpandedTextWidth();\n            } else if (iconPosition == QMUITab.ICON_POSITION_BOTTOM ||\n                    iconPosition == QMUITab.ICON_POSITION_TOP) {\n                widthSize = (int) Math.max(\n                        mTab.getNormalTabIconWidth() * mTab.getSelectedTabIconScale(),\n                        mCollapsingTextHelper.getExpandedTextWidth());\n            } else {\n                widthSize = (int) (mCollapsingTextHelper.getExpandedTextWidth() +\n                        mTab.getIconTextGap() +\n                        mTab.getNormalTabIconWidth() * mTab.getSelectedTabIconScale());\n            }\n            if(mSignCountView != null && mSignCountView.getVisibility() != View.GONE){\n                mSignCountView.measure(0, 0);\n                widthSize = Math.max(widthSize,\n                        widthSize + mSignCountView.getMeasuredWidth() + mTab.signCountHorizontalOffset);\n            }\n            useWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);\n        }\n        if (heightMode == MeasureSpec.AT_MOST) {\n            if (icon == null) {\n                heightSize = (int) mCollapsingTextHelper.getExpandedTextHeight();\n            } else if (iconPosition == QMUITab.ICON_POSITION_LEFT ||\n                    iconPosition == QMUITab.ICON_POSITION_RIGHT) {\n                heightSize = (int) Math.max(\n                        mTab.getNormalTabIconHeight() * mTab.getSelectedTabIconScale(),\n                        mCollapsingTextHelper.getExpandedTextWidth());\n            } else {\n                heightSize = (int) (mCollapsingTextHelper.getExpandedTextHeight() +\n                        mTab.getIconTextGap() +\n                        mTab.getNormalTabIconHeight() * mTab.getSelectedTabIconScale());\n            }\n            useHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);\n        }\n        super.onMeasure(useWidthMeasureSpec, useHeightMeasureSpec);\n    }\n\n    protected void onMeasureTab(int widthSize, int heightSize) {\n        int textWidth = widthSize, textHeight = heightSize;\n        QMUITabIcon icon = mTab.getTabIcon();\n        if (icon != null && !mTab.isAllowIconDrawOutside()) {\n            float iconWidth = mTab.getNormalTabIconWidth() * mTab.selectedTabIconScale;\n            float iconHeight = mTab.getNormalTabIconHeight() * mTab.selectedTabIconScale;\n            int iconPosition = mTab.iconPosition;\n            if (iconPosition == QMUITab.ICON_POSITION_TOP || iconPosition == QMUITab.ICON_POSITION_BOTTOM) {\n                textHeight -= iconHeight - mTab.getIconTextGap();\n            } else {\n                textWidth -= iconWidth - mTab.getIconTextGap();\n            }\n        }\n        mCollapsingTextHelper.setCollapsedBounds(0, 0, textWidth, textHeight);\n        mCollapsingTextHelper.setExpandedBounds(0, 0, textWidth, textHeight);\n        mCollapsingTextHelper.calculateBaseOffsets();\n    }\n\n    @Override\n    protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        onLayoutTab(right - left, bottom - top);\n        onLayoutSignCount(right - left, bottom - top);\n    }\n\n    protected void onLayoutSignCount(int width, int height) {\n        if (mSignCountView != null && mTab != null) {\n            Point point = calculateSignCountLayoutPosition();\n            int x = point.x, y = point.y;\n            if (point.x + mSignCountView.getMeasuredWidth() > width) {\n                x = width - mSignCountView.getMeasuredWidth();\n            }\n\n            if (point.y - mSignCountView.getMeasuredHeight() < 0) {\n                y = mSignCountView.getMeasuredHeight();\n            }\n            mSignCountView.layout(x, y - mSignCountView.getMeasuredHeight(),\n                    x + mSignCountView.getMeasuredWidth(), y);\n        }\n    }\n\n    private Point calculateSignCountLayoutPosition() {\n        QMUITabIcon icon = mTab.getTabIcon();\n        int anchorLeft, anchorTop;\n        int iconPosition = mTab.getIconPosition();\n        if (icon == null || iconPosition == QMUITab.ICON_POSITION_BOTTOM ||\n                iconPosition == QMUITab.ICON_POSITION_LEFT) {\n            anchorLeft = (int) (mCurrentTextLeft + mCurrentTextWidth);\n            anchorTop = (int) (mCurrentTextTop);\n        } else {\n            anchorLeft = (int) (mCurrentIconLeft + mCurrentIconWidth);\n            anchorTop = (int) (mCurrentIconTop);\n        }\n        Point point = new Point(anchorLeft, anchorTop);\n        int verticalAlign = mTab.signCountVerticalAlign;\n        int verticalOffset = mTab.signCountVerticalOffset;\n        if(verticalAlign == QMUITab.SIGN_COUNT_VERTICAL_ALIGN_TOP_TO_CONTENT_TOP){\n            point.offset(mTab.signCountHorizontalOffset, verticalOffset + mSignCountView.getMeasuredHeight());\n        }else if(verticalAlign == QMUITab.SIGN_COUNT_VERTICAL_ALIGN_MIDDLE_TO_CONTENT){\n            point.y = getMeasuredHeight() - (getMeasuredHeight() - mSignCountView.getMeasuredHeight()) / 2;\n            point.offset(mTab.signCountHorizontalOffset, verticalOffset);\n        }else {\n            point.offset(mTab.signCountHorizontalOffset, verticalOffset);\n        }\n\n\n        return point;\n    }\n\n    protected void onLayoutTab(int width, int height) {\n        if (mTab == null) {\n            return;\n        }\n        mCollapsingTextHelper.calculateCurrentOffsets();\n        QMUITabIcon icon = mTab.getTabIcon();\n        float normalTextWidth = mCollapsingTextHelper.getCollapsedTextWidth();\n        float normalTextHeight = mCollapsingTextHelper.getCollapsedTextHeight();\n\n        float selectedTextWidth = mCollapsingTextHelper.getExpandedTextWidth();\n        float selectedTextHeight = mCollapsingTextHelper.getExpandedTextHeight();\n\n        if (icon == null) {\n            mNormalIconLeft = mNormalIconTop = mSelectedIconLeft = mSelectedIconTop = 0;\n            switch (mTab.gravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                case Gravity.BOTTOM:\n                    mNormalTextTop = height - normalTextHeight;\n                    mSelectedTextTop = height - selectedTextHeight;\n                    break;\n                case Gravity.TOP:\n                    mNormalTextTop = 0;\n                    mSelectedTextTop = 0;\n                    break;\n                case Gravity.CENTER_VERTICAL:\n                default:\n                    mNormalTextTop = (height - normalTextHeight) / 2;\n                    mSelectedTextTop = (height - selectedTextHeight) / 2;\n                    break;\n            }\n\n            switch (mTab.gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n                case Gravity.RIGHT:\n                    mNormalTextLeft = width - normalTextWidth;\n                    mSelectedTextLeft = width - selectedTextWidth;\n                    break;\n                case Gravity.LEFT:\n                    mNormalTextLeft = 0;\n                    mSelectedTextLeft = 0;\n                    break;\n                case Gravity.CENTER_HORIZONTAL:\n                default:\n                    mNormalTextLeft = (width - normalTextWidth) / 2;\n                    mSelectedTextLeft = (width - selectedTextWidth) / 2;\n                    break;\n            }\n        } else {\n            int gap = mTab.getIconTextGap();\n            int iconPosition = mTab.iconPosition;\n\n            // icon\n            float normalIconWidth = mTab.getNormalTabIconWidth();\n            float normalIconHeight = mTab.getNormalTabIconHeight();\n            float selectedIconWidth = normalIconWidth * mTab.getSelectedTabIconScale();\n            float selectedIconHeight = normalIconHeight * mTab.getSelectedTabIconScale();\n\n            // total size\n            float normalTotalWidth = normalTextWidth + gap + normalIconWidth;\n            float normalTotalHeight = normalTextHeight + gap + normalIconHeight;\n            float selectedTotalWidth = selectedTextWidth + gap + selectedIconWidth;\n            float selectedTotalHeight = selectedTextHeight + gap + selectedIconHeight;\n\n            if (iconPosition == QMUITab.ICON_POSITION_TOP || iconPosition == QMUITab.ICON_POSITION_BOTTOM) {\n                switch (mTab.gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n                    case Gravity.RIGHT:\n                        mNormalIconLeft = width - normalIconWidth;\n                        mNormalTextLeft = width - normalTextWidth;\n                        mSelectedIconLeft = width - selectedIconWidth;\n                        mSelectedTextLeft = width - selectedTextWidth;\n                        break;\n                    case Gravity.LEFT:\n                        mNormalIconLeft = 0;\n                        mNormalTextLeft = 0;\n                        mSelectedIconLeft = 0;\n                        mSelectedTextLeft = 0;\n                        break;\n                    case Gravity.CENTER_HORIZONTAL:\n                    default:\n                        mNormalIconLeft = (width - normalIconWidth) / 2;\n                        mNormalTextLeft = (width - normalTextWidth) / 2;\n                        mSelectedIconLeft = (width - selectedIconWidth) / 2;\n                        mSelectedTextLeft = (width - selectedTextWidth) / 2;\n                        break;\n                }\n\n                switch (mTab.gravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                    case Gravity.BOTTOM:\n                        if (iconPosition == QMUITab.ICON_POSITION_TOP) {\n                            mNormalTextTop = height - normalTextHeight;\n                            mSelectedTextTop = height - selectedTextHeight;\n                            mNormalIconTop = mNormalTextTop - gap - normalIconHeight;\n                            mSelectedIconTop = mSelectedTextTop - gap - selectedIconHeight;\n                        } else {\n                            mNormalIconTop = height - normalIconHeight;\n                            mSelectedIconTop = height - selectedIconHeight;\n                            mNormalTextTop = mNormalIconTop - gap - normalTextHeight;\n                            mSelectedTextTop = mSelectedIconTop - gap - selectedTextHeight;\n                        }\n                        break;\n                    case Gravity.TOP:\n                        if (iconPosition == QMUITab.ICON_POSITION_TOP) {\n                            mNormalIconTop = 0;\n                            mSelectedIconTop = 0;\n                            mNormalTextTop = normalIconHeight + gap;\n                            mSelectedTextTop = selectedIconHeight + gap;\n                        } else {\n                            mNormalTextTop = 0;\n                            mSelectedTextTop = 0;\n                            mNormalIconTop = normalTextHeight + gap;\n                            mSelectedIconTop = selectedTextHeight + gap;\n                        }\n                        break;\n                    case Gravity.CENTER_VERTICAL:\n                    default:\n                        // if the space is not enough, keep text\n                        if (iconPosition == QMUITab.ICON_POSITION_TOP) {\n                            // normal\n                            if (normalTotalHeight >= height) {\n                                mNormalIconTop = height - normalTotalHeight;\n                            } else {\n                                mNormalIconTop = (height - normalTotalHeight) / 2;\n                            }\n                            mNormalTextTop = mNormalIconTop + gap + normalIconHeight;\n\n                            // selected\n                            if (selectedTotalHeight >= height) {\n                                mSelectedIconTop = height - selectedTotalHeight;\n                            } else {\n                                mSelectedIconTop = (height - selectedTotalHeight) / 2;\n                            }\n                            mSelectedTextTop = mSelectedIconTop + gap + selectedIconHeight;\n                        } else {\n                            // normal\n                            if (normalTotalHeight >= height) {\n                                mNormalTextTop = 0;\n                            } else {\n                                mNormalTextTop = (height - normalTotalHeight) / 2;\n                            }\n                            mNormalIconTop = mNormalTextTop + gap + normalTextHeight;\n\n                            // selected\n                            if (selectedTotalHeight >= height) {\n                                mNormalTextTop = 0;\n                            } else {\n                                mNormalTextTop = (height - selectedTotalHeight) / 2;\n                            }\n                            mNormalIconTop = mNormalTextTop + gap + selectedTextHeight;\n                        }\n                        break;\n                }\n            } else {\n                switch (mTab.gravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                    case Gravity.BOTTOM:\n                        mNormalIconTop = height - normalIconHeight;\n                        mNormalTextTop = height - normalTextHeight;\n                        mSelectedIconTop = height - selectedIconHeight;\n                        mSelectedTextTop = height - selectedTextHeight;\n                        break;\n                    case Gravity.TOP:\n                        mNormalIconTop = 0;\n                        mNormalTextTop = 0;\n                        mSelectedIconTop = 0;\n                        mSelectedTextTop = 0;\n                        break;\n                    case Gravity.CENTER_VERTICAL:\n                    default:\n                        mNormalIconTop = (height - normalIconHeight) / 2;\n                        mNormalTextTop = (height - normalTextHeight) / 2;\n                        mSelectedIconTop = (height - selectedIconHeight) / 2;\n                        mSelectedTextTop = (height - selectedTextHeight) / 2;\n                        break;\n                }\n\n                switch (mTab.gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n                    case Gravity.RIGHT:\n                        if (iconPosition == QMUITab.ICON_POSITION_RIGHT) {\n                            mNormalTextLeft = width - normalTotalWidth;\n                            mSelectedTextLeft = width - selectedTotalWidth;\n                            mNormalIconLeft = width - normalIconWidth;\n                            mSelectedIconLeft = width - selectedIconWidth;\n                        } else {\n                            mNormalIconLeft = width - normalTotalWidth;\n                            mSelectedIconLeft = width - selectedTotalWidth;\n                            mNormalTextLeft = width - normalTextWidth;\n                            mSelectedTextLeft = width - selectedTextWidth;\n                        }\n                        break;\n                    case Gravity.LEFT:\n                        if (iconPosition == QMUITab.ICON_POSITION_RIGHT) {\n                            mNormalTextLeft = 0;\n                            mSelectedTextLeft = 0;\n                            mNormalIconLeft = normalTextWidth + gap;\n                            mSelectedIconLeft = selectedTextWidth + gap;\n                        } else {\n                            mNormalIconLeft = 0;\n                            mSelectedIconLeft = 0;\n                            mNormalTextLeft = normalIconWidth + gap;\n                            mSelectedTextLeft = selectedIconWidth + gap;\n                        }\n                        break;\n                    case Gravity.CENTER_HORIZONTAL:\n                    default:\n                        if (iconPosition == QMUITab.ICON_POSITION_RIGHT) {\n                            mNormalTextLeft = (width - normalTotalWidth) / 2;\n                            mSelectedTextLeft = (width - selectedTotalWidth) / 2;\n                            mNormalIconLeft = mNormalTextLeft + normalTextWidth + gap;\n                            mSelectedIconLeft = mSelectedTextLeft + selectedTextWidth + gap;\n                        } else {\n                            mNormalIconLeft = (width - normalTotalWidth) / 2;\n                            mSelectedIconLeft = (width - selectedTotalWidth) / 2;\n                            mNormalTextLeft = mNormalIconLeft + normalIconWidth + gap;\n                            mSelectedTextLeft = mSelectedIconLeft + selectedIconWidth + gap;\n                        }\n                        break;\n                }\n\n                if (iconPosition == QMUITab.ICON_POSITION_LEFT) {\n                    // normal\n                    if (normalTotalWidth >= width) {\n                        mNormalIconLeft = width - normalTotalWidth;\n                    } else {\n                        mNormalIconLeft = (width - normalTotalWidth) / 2;\n                    }\n                    mNormalTextLeft = mNormalIconLeft + normalIconWidth + gap;\n\n                    // selected\n                    if (selectedTotalWidth >= width) {\n                        mSelectedIconLeft = width - selectedTotalWidth;\n                    } else {\n                        mSelectedIconLeft = (width - selectedTotalWidth) / 2;\n                    }\n                    mSelectedTextLeft = mSelectedIconLeft + selectedIconWidth + gap;\n                } else {\n                    // normal\n                    if (normalTotalWidth >= width) {\n                        mNormalTextLeft = 0;\n                    } else {\n                        mNormalTextLeft = (width - normalTotalWidth) / 2;\n                    }\n                    mNormalIconLeft = mNormalTextLeft + normalTextWidth + gap;\n\n                    // selected\n                    if (selectedTotalWidth >= width) {\n                        mSelectedTextLeft = 0;\n                    } else {\n                        mSelectedTextLeft = (width - selectedTotalWidth) / 2;\n                    }\n                    mSelectedIconLeft = mSelectedTextLeft + selectedTextWidth + gap;\n                }\n            }\n        }\n        updateCurrentInfo(1 - mCollapsingTextHelper.getExpansionFraction());\n    }\n\n    @Override\n    public void invalidateDrawable(@NonNull Drawable drawable) {\n        invalidate();\n    }\n\n    @Override\n    public final void draw(Canvas canvas) {\n        onDrawTab(canvas);\n        super.draw(canvas);\n    }\n    \n    @Override\n    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {\n        super.onInitializeAccessibilityNodeInfo(info);\n\n        // 给每个tab添加文本标签\n        // 使得TalkBack等屏幕阅读器focus 到 tab上时可将tab的文本通过TTS朗读出来\n        // 这样视力受损用户（如盲人、低、弱视力）就能和widget交互\n        info.setContentDescription(mTab.getText());\n    }\n\n    protected void onDrawTab(Canvas canvas) {\n        if (mTab == null) {\n            return;\n        }\n        QMUITabIcon icon = mTab.getTabIcon();\n        if (icon != null) {\n            canvas.save();\n            canvas.translate(mCurrentIconLeft, mCurrentIconTop);\n            icon.setBounds(0, 0, (int) mCurrentIconWidth, (int) mCurrentIconHeight);\n            icon.draw(canvas);\n            canvas.restore();\n        }\n\n        canvas.save();\n        canvas.translate(mCurrentTextLeft, mCurrentTextTop);\n        mCollapsingTextHelper.draw(canvas);\n        canvas.restore();\n    }\n\n    @Override\n    public void handle(@NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme, @Nullable SimpleArrayMap<String, Integer> attrs) {\n        if (mTab != null) {\n            updateSkinInfo(mTab);\n            invalidate();\n        }\n    }\n\n    private void updateSkinInfo(QMUITab tab) {\n        int normalColor = tab.getNormalColor(this);\n        int selectedColor = tab.getSelectColor(this);\n        mCollapsingTextHelper.setTextColor(\n                ColorStateList.valueOf(normalColor),\n                ColorStateList.valueOf(selectedColor),\n                true);\n        if (tab.tabIcon != null) {\n            if (tab.skinChangeWithTintColor || (tab.skinChangeNormalWithTintColor && tab.skinChangeSelectedWithTintColor)) {\n                tab.tabIcon.tint(normalColor, selectedColor);\n            } else {\n                if(tab.tabIcon.hasSelectedIcon()){\n                    if(tab.skinChangeNormalWithTintColor){\n                        tab.tabIcon.tintNormal(normalColor);\n                    }else{\n                        if(tab.normalIconAttr != 0){\n                            Drawable normalIcon = QMUISkinHelper.getSkinDrawable(this, tab.normalIconAttr);\n                            if(normalIcon != null){\n                                tab.tabIcon.srcNormal(normalIcon);\n                            }\n                        }\n                    }\n\n                    if(tab.skinChangeSelectedWithTintColor){\n                        tab.tabIcon.tintSelected(normalColor);\n                    }else{\n                        if(tab.selectedIconAttr != 0){\n                            Drawable selectedIcon = QMUISkinHelper.getSkinDrawable(this, tab.selectedIconAttr);\n                            if(selectedIcon != null){\n                                tab.tabIcon.srcSelected(selectedIcon);\n                            }\n                        }\n                    }\n                }else{\n                    if(tab.skinChangeNormalWithTintColor){\n                        tab.tabIcon.tint(normalColor, selectedColor);\n                    }else{\n                        if(tab.normalIconAttr != 0){\n                            Drawable normalIcon = QMUISkinHelper.getSkinDrawable(this, tab.normalIconAttr);\n                            if(normalIcon != null){\n                                tab.tabIcon.src(normalIcon, normalColor, selectedColor);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    public interface Callback {\n        void onClick(QMUITabView view);\n\n        void onDoubleClick(QMUITabView view);\n\n        void onLongClick(QMUITabView view);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/textview/ISpanTouchFix.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.textview;\n\n/**\n * @author cginechen\n * @date 2017-08-07\n */\n\npublic interface ISpanTouchFix {\n    /**\n     * 记录当前 Touch 事件对应的点是不是点在了 span 上面\n     */\n    void setTouchSpanHit(boolean hit);\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/textview/QMUILinkTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.textview;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.SystemClock;\n\nimport androidx.core.content.ContextCompat;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.ViewConfiguration;\n\nimport com.qmuiteam.qmui.R;\nimport com.qmuiteam.qmui.alpha.QMUIAlphaTextView;\nimport com.qmuiteam.qmui.link.QMUILinkTouchMovementMethod;\nimport com.qmuiteam.qmui.link.QMUILinkify;\nimport com.qmuiteam.qmui.span.QMUIOnSpanClickListener;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 使 {@link android.widget.TextView} 能自动识别 URL、电话、邮箱地址。\n * 相比于 {@link android.widget.TextView} 使用 {@link android.text.util.Linkify},\n * {@link QMUILinkTextView} 有以下特点:\n * <ul>\n * <li>可以通过 {@link com.qmuiteam.qmui.R.styleable#QMUILinkTextView} 中的属性设置链接的样式</li>\n * <li>可以通过 {@link QMUILinkTextView#setOnLinkClickListener(QMUILinkTextView.OnLinkClickListener)} 设置链接的点击事件，\n * 而不是 {@link android.widget.TextView} 默认的 {@link android.content.Intent} 跳转</li>\n * </ul>\n *\n * @author cginechen\n * @date 2017-03-17\n */\npublic class QMUILinkTextView extends QMUIAlphaTextView implements QMUIOnSpanClickListener {\n    private static final String TAG = \"LinkTextView\";\n    private static final int MSG_CHECK_DOUBLE_TAP_TIMEOUT = 1000;\n    public static int AUTO_LINK_MASK_REQUIRED = QMUILinkify.PHONE_NUMBERS | QMUILinkify.EMAIL_ADDRESSES | QMUILinkify.WEB_URLS;\n    private static Set<String> AUTO_LINK_SCHEME_INTERRUPTED = new HashSet<>();\n    private CharSequence mOriginText = null;\n\n    static {\n        AUTO_LINK_SCHEME_INTERRUPTED.add(\"tel\");\n        AUTO_LINK_SCHEME_INTERRUPTED.add(\"mailto\");\n        AUTO_LINK_SCHEME_INTERRUPTED.add(\"http\");\n        AUTO_LINK_SCHEME_INTERRUPTED.add(\"https\");\n    }\n\n    /**\n     * 链接文字颜色\n     */\n    private ColorStateList mLinkTextColor;\n    /**\n     * 链接背景颜色\n     */\n    private ColorStateList mLinkBgColor;\n\n    private int mAutoLinkMaskCompat;\n    private OnLinkClickListener mOnLinkClickListener;\n    private OnLinkLongClickListener mOnLinkLongClickListener;\n\n    private long mDownMillis = 0;\n    private static final long TAP_TIMEOUT = 200; // ViewConfiguration.getTapTimeout();\n    private static final long DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();\n\n    public QMUILinkTextView(Context context) {\n        this(context, null);\n        mLinkBgColor = null;\n        mLinkTextColor = ContextCompat.getColorStateList(context, R.color.qmui_s_link_color);\n    }\n\n    public QMUILinkTextView(Context context, ColorStateList linkTextColor, ColorStateList linkBgColor) {\n        this(context, null);\n        mLinkBgColor = linkBgColor;\n        mLinkTextColor = linkTextColor;\n    }\n\n    public QMUILinkTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        mAutoLinkMaskCompat = getAutoLinkMask() | AUTO_LINK_MASK_REQUIRED;\n        setAutoLinkMask(0);\n        setMovementMethodCompat(QMUILinkTouchMovementMethod.getInstance());\n        setHighlightColor(Color.TRANSPARENT);\n        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QMUILinkTextView);\n        mLinkBgColor = array.getColorStateList(R.styleable.QMUILinkTextView_qmui_linkBackgroundColor);\n        mLinkTextColor = array.getColorStateList(R.styleable.QMUILinkTextView_qmui_linkTextColor);\n        array.recycle();\n        if (mOriginText != null) {\n            setText(mOriginText);\n        }\n        setChangeAlphaWhenPress(false);\n    }\n\n    public void setOnLinkClickListener(OnLinkClickListener onLinkClickListener) {\n        mOnLinkClickListener = onLinkClickListener;\n    }\n\n    public void setOnLinkLongClickListener(OnLinkLongClickListener onLinkLongClickListener) {\n        mOnLinkLongClickListener = onLinkLongClickListener;\n    }\n\n    public int getAutoLinkMaskCompat() {\n        return mAutoLinkMaskCompat;\n    }\n\n    public void setAutoLinkMaskCompat(int mask) {\n        mAutoLinkMaskCompat = mask;\n    }\n\n    public void addAutoLinkMaskCompat(int mask) {\n        mAutoLinkMaskCompat |= mask;\n    }\n\n    public void removeAutoLinkMaskCompat(int mask) {\n        mAutoLinkMaskCompat &= ~mask;\n    }\n\n\n    public void setLinkColor(ColorStateList linkTextColor) {\n        mLinkTextColor = linkTextColor;\n    }\n\n    @Override\n    public void setText(CharSequence text, BufferType type) {\n        mOriginText = text;\n        if (!TextUtils.isEmpty(text)) {\n            SpannableStringBuilder builder = new SpannableStringBuilder(text);\n            QMUILinkify.addLinks(builder, mAutoLinkMaskCompat, mLinkTextColor, mLinkBgColor, this);\n            text = builder;\n        }\n        super.setText(text, type);\n    }\n\n    @Override\n    public boolean onSpanClick(String text) {\n        if (null == text) {\n            Log.w(TAG, \"onSpanClick interrupt null text\");\n            return true;\n        }\n        long clickUpTime = (SystemClock.uptimeMillis() - mDownMillis);\n        Log.w(TAG, \"onSpanClick clickUpTime: \" + clickUpTime);\n        if (mSingleTapConfirmedHandler.hasMessages(MSG_CHECK_DOUBLE_TAP_TIMEOUT)) {\n            disallowOnSpanClickInterrupt();\n            return true;\n        }\n\n        if (TAP_TIMEOUT < clickUpTime) {\n            Log.w(TAG, \"onSpanClick interrupted because of TAP_TIMEOUT: \" + clickUpTime);\n            return true;\n        }\n\n        String scheme = Uri.parse(text).getScheme();\n        if (scheme != null) {\n            scheme = scheme.toLowerCase();\n        }\n\n        if (AUTO_LINK_SCHEME_INTERRUPTED.contains(scheme)) {\n            long waitTime = DOUBLE_TAP_TIMEOUT - clickUpTime;\n            mSingleTapConfirmedHandler.removeMessages(MSG_CHECK_DOUBLE_TAP_TIMEOUT);\n            Message msg = Message.obtain();\n            msg.what = MSG_CHECK_DOUBLE_TAP_TIMEOUT;\n            msg.obj = text;\n            mSingleTapConfirmedHandler.sendMessageDelayed(msg, waitTime);\n            return true;\n        }\n        return false;\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        switch (event.getAction() & MotionEvent.ACTION_MASK) {\n            case MotionEvent.ACTION_DOWN:\n                boolean hasSingleTap = mSingleTapConfirmedHandler.hasMessages(MSG_CHECK_DOUBLE_TAP_TIMEOUT);\n                Log.w(TAG, \"onTouchEvent hasSingleTap: \" + hasSingleTap);\n                if (!hasSingleTap) {\n                    mDownMillis = SystemClock.uptimeMillis();\n                } else {\n                    Log.w(TAG, \"onTouchEvent disallow onSpanClick mSingleTapConfirmedHandler because of DOUBLE TAP\");\n                    disallowOnSpanClickInterrupt();\n                }\n                break;\n        }\n        return super.onTouchEvent(event);\n    }\n\n    private void disallowOnSpanClickInterrupt() {\n        mSingleTapConfirmedHandler.removeMessages(MSG_CHECK_DOUBLE_TAP_TIMEOUT);\n        mDownMillis = 0;\n    }\n\n    protected boolean performSpanLongClick(String text) {\n        if (mOnLinkLongClickListener != null) {\n            mOnLinkLongClickListener.onLongClick(text);\n            return true;\n        }\n        return false;\n    }\n\n\n    @Override\n    public boolean performLongClick() {\n        int end = getSelectionEnd();\n\n        if (end > 0) {\n\n            String selectStr = getText().subSequence(getSelectionStart(), end).toString();\n            return performSpanLongClick(selectStr) || super.performLongClick();\n\n        }\n        return super.performLongClick();\n    }\n\n    private Handler mSingleTapConfirmedHandler = new Handler(Looper.getMainLooper()) {\n\n        public void handleMessage(android.os.Message msg) {\n            if (MSG_CHECK_DOUBLE_TAP_TIMEOUT != msg.what) {\n                return;\n            }\n\n            Log.d(TAG, \"handleMessage: \" + msg.obj);\n            if (msg.obj instanceof String) {\n                String url = (String) msg.obj;\n                if (null != mOnLinkClickListener && !TextUtils.isEmpty(url)) {\n                    String schemeUrl = url.toLowerCase();\n                    if (schemeUrl.startsWith(\"tel:\")) {\n                        String phoneNumber = Uri.parse(url).getSchemeSpecificPart();\n                        mOnLinkClickListener.onTelLinkClick(phoneNumber);\n                    } else if (schemeUrl.startsWith(\"mailto:\")) {\n                        String mailAddr = Uri.parse(url).getSchemeSpecificPart();\n                        mOnLinkClickListener.onMailLinkClick(mailAddr);\n                    } else if (schemeUrl.startsWith(\"http\") || schemeUrl.startsWith(\"https\")) {\n                        mOnLinkClickListener.onWebUrlLinkClick(url);\n                    }\n                }\n            }\n        }\n\n    };\n\n    public interface OnLinkClickListener {\n\n        /**\n         * 电话号码被点击\n         */\n        void onTelLinkClick(String phoneNumber);\n\n        /**\n         * 邮箱地址被点击\n         */\n        void onMailLinkClick(String mailAddress);\n\n        /**\n         * URL 被点击\n         */\n        void onWebUrlLinkClick(String url);\n\n    }\n\n    public interface OnLinkLongClickListener {\n        void onLongClick(String text);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/textview/QMUISpanTouchFixTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.textview;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.text.Spannable;\nimport android.text.method.MovementMethod;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.widget.TextView;\n\nimport androidx.annotation.ColorInt;\nimport androidx.appcompat.widget.AppCompatTextView;\n\nimport com.qmuiteam.qmui.layout.IQMUILayout;\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\nimport com.qmuiteam.qmui.link.QMUILinkTouchMovementMethod;\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\n\n/**\n * <p>\n * 修复了 {@link TextView} 与 {@link android.text.style.ClickableSpan} 一起使用时，\n * 点击 {@link android.text.style.ClickableSpan} 也会触发 {@link TextView} 的事件的问题。\n * </p>\n * <p>\n * 同时通过 {@link #setNeedForceEventToParent(boolean)} 控制该 TextView 的点击事件能否传递给其 Parent，\n * 修复了 {@link TextView} 默认情况下如果添加了 {@link android.text.style.ClickableSpan} 之后就无法把点击事件传递给 {@link TextView} 的 Parent 的问题。\n * </p>\n * <p>\n * 注意: 使用该 {@link TextView} 时, 用 {@link QMUITouchableSpan} 代替 {@link android.text.style.ClickableSpan},\n * 且同时可以使用 {@link QMUITouchableSpan} 达到修改 span 的文字颜色和背景色的目的。\n * </p>\n * <p>\n * 注意: 使用该 {@link TextView} 时, 需调用 {@link #setMovementMethodDefault()} 方法设置默认的 {@link QMUILinkTouchMovementMethod},\n * TextView 会在 {@link #onTouchEvent(MotionEvent)} 时将事件传递给 {@link QMUILinkTouchMovementMethod},\n * 然后传递给 {@link QMUITouchableSpan}, 实现点击态的变化和点击事件的响应。\n * </p>\n *\n * @author cginechen\n * @date 2017-03-20\n * @see QMUITouchableSpan\n * @see QMUILinkTouchMovementMethod\n */\npublic class QMUISpanTouchFixTextView extends AppCompatTextView implements ISpanTouchFix, IQMUILayout {\n    /**\n     * 记录当前 Touch 事件对应的点是不是点在了 span 上面\n     */\n    private boolean mTouchSpanHit;\n\n    /**\n     * 记录每次真正传入的press，每次更改mTouchSpanHint，需要再调用一次setPressed，确保press状态正确\n     */\n    private boolean mIsPressedRecord = false;\n    /**\n     * TextView是否应该消耗事件\n     */\n    private boolean mNeedForceEventToParent = false;\n\n    private QMUILayoutHelper mLayoutHelper;\n\n    public QMUISpanTouchFixTextView(Context context) {\n        this(context, null);\n    }\n\n    public QMUISpanTouchFixTextView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public QMUISpanTouchFixTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setHighlightColor(Color.TRANSPARENT);\n        mLayoutHelper = new QMUILayoutHelper(context, attrs, defStyleAttr, this);\n    }\n\n    public void setNeedForceEventToParent(boolean needForceEventToParent) {\n        mNeedForceEventToParent = needForceEventToParent;\n        setFocusable(!needForceEventToParent);\n        setClickable(!needForceEventToParent);\n        setLongClickable(!needForceEventToParent);\n    }\n\n    /**\n     * 使用者主动调用\n     */\n    public void setMovementMethodDefault(){\n        setMovementMethodCompat(QMUILinkTouchMovementMethod.getInstance());\n    }\n\n    public void setMovementMethodCompat(MovementMethod movement){\n        setMovementMethod(movement);\n        if(mNeedForceEventToParent){\n            setNeedForceEventToParent(true);\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!(getText() instanceof Spannable) || !(getMovementMethod() instanceof QMUILinkTouchMovementMethod)) {\n            mTouchSpanHit = false;\n            return super.onTouchEvent(event);\n        }\n        mTouchSpanHit = true;\n        // 调用super.onTouchEvent,会走到QMUILinkTouchMovementMethod\n        // 会走到QMUILinkTouchMovementMethod#onTouchEvent会修改mTouchSpanHint\n        boolean ret = super.onTouchEvent(event);\n        if(mNeedForceEventToParent){\n            return mTouchSpanHit;\n        }\n        return ret;\n    }\n\n    @Override\n    public void setTouchSpanHit(boolean hit) {\n        if (mTouchSpanHit != hit) {\n            mTouchSpanHit = hit;\n            setPressed(mIsPressedRecord);\n        }\n    }\n\n    @SuppressWarnings(\"SimplifiableIfStatement\")\n    @Override\n    public boolean performClick() {\n        if (!mTouchSpanHit && !mNeedForceEventToParent) {\n            return super.performClick();\n        }\n        return false;\n    }\n\n    @SuppressWarnings(\"SimplifiableIfStatement\")\n    @Override\n    public boolean performLongClick() {\n        if (!mTouchSpanHit && !mNeedForceEventToParent) {\n            return super.performLongClick();\n        }\n        return false;\n    }\n\n    @Override\n    public final void setPressed(boolean pressed) {\n        mIsPressedRecord = pressed;\n        if (!mTouchSpanHit) {\n            onSetPressed(pressed);\n        }\n    }\n\n    protected void onSetPressed(boolean pressed) {\n        super.setPressed(pressed);\n    }\n\n    @Override\n    public void updateTopDivider(int topInsetLeft, int topInsetRight, int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.updateTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateBottomDivider(int bottomInsetLeft, int bottomInsetRight, int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.updateBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void updateLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.updateLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    public void updateRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.updateRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowTopDivider(int topInsetLeft, int topInsetRight,\n                                   int topDividerHeight, int topDividerColor) {\n        mLayoutHelper.onlyShowTopDivider(topInsetLeft, topInsetRight, topDividerHeight, topDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowBottomDivider(int bottomInsetLeft, int bottomInsetRight,\n                                      int bottomDividerHeight, int bottomDividerColor) {\n        mLayoutHelper.onlyShowBottomDivider(bottomInsetLeft, bottomInsetRight, bottomDividerHeight, bottomDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowLeftDivider(int leftInsetTop, int leftInsetBottom, int leftDividerWidth, int leftDividerColor) {\n        mLayoutHelper.onlyShowLeftDivider(leftInsetTop, leftInsetBottom, leftDividerWidth, leftDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void onlyShowRightDivider(int rightInsetTop, int rightInsetBottom, int rightDividerWidth, int rightDividerColor) {\n        mLayoutHelper.onlyShowRightDivider(rightInsetTop, rightInsetBottom, rightDividerWidth, rightDividerColor);\n        invalidate();\n    }\n\n    @Override\n    public void setTopDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setTopDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setBottomDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setBottomDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setLeftDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setLeftDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setRightDividerAlpha(int dividerAlpha) {\n        mLayoutHelper.setRightDividerAlpha(dividerAlpha);\n        invalidate();\n    }\n\n    @Override\n    public void setHideRadiusSide(int hideRadiusSide) {\n        mLayoutHelper.setHideRadiusSide(hideRadiusSide);\n        invalidate();\n    }\n\n    @Override\n    public int getHideRadiusSide() {\n        return mLayoutHelper.getHideRadiusSide();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = mLayoutHelper.getMeasuredWidthSpec(widthMeasureSpec);\n        heightMeasureSpec = mLayoutHelper.getMeasuredHeightSpec(heightMeasureSpec);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int minW = mLayoutHelper.handleMiniWidth(widthMeasureSpec, getMeasuredWidth());\n        int minH = mLayoutHelper.handleMiniHeight(heightMeasureSpec, getMeasuredHeight());\n        if (widthMeasureSpec != minW || heightMeasureSpec != minH) {\n            super.onMeasure(minW, minH);\n        }\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide, int shadowElevation, final float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation, shadowAlpha);\n    }\n\n    @Override\n    public void setRadiusAndShadow(int radius, int hideRadiusSide, int shadowElevation, int shadowColor, float shadowAlpha) {\n        mLayoutHelper.setRadiusAndShadow(radius, hideRadiusSide, shadowElevation,  shadowColor, shadowAlpha);\n    }\n\n    @Override\n    public void setRadius(int radius) {\n        mLayoutHelper.setRadius(radius);\n    }\n\n    @Override\n    public void setRadius(int radius, @QMUILayoutHelper.HideRadiusSide int hideRadiusSide) {\n        mLayoutHelper.setRadius(radius, hideRadiusSide);\n    }\n\n    @Override\n    public int getRadius() {\n        return mLayoutHelper.getRadius();\n    }\n\n    @Override\n    public void setOutlineInset(int left, int top, int right, int bottom) {\n        mLayoutHelper.setOutlineInset(left, top, right, bottom);\n    }\n\n    @Override\n    public void setBorderColor(@ColorInt int borderColor) {\n        mLayoutHelper.setBorderColor(borderColor);\n        invalidate();\n    }\n\n    @Override\n    public void setBorderWidth(int borderWidth) {\n        mLayoutHelper.setBorderWidth(borderWidth);\n        invalidate();\n    }\n\n    @Override\n    public void setShowBorderOnlyBeforeL(boolean showBorderOnlyBeforeL) {\n        mLayoutHelper.setShowBorderOnlyBeforeL(showBorderOnlyBeforeL);\n        invalidate();\n    }\n\n    @Override\n    public boolean setWidthLimit(int widthLimit) {\n        if (mLayoutHelper.setWidthLimit(widthLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean setHeightLimit(int heightLimit) {\n        if (mLayoutHelper.setHeightLimit(heightLimit)) {\n            requestLayout();\n            invalidate();\n        }\n        return true;\n    }\n\n    @Override\n    public void setUseThemeGeneralShadowElevation() {\n        mLayoutHelper.setUseThemeGeneralShadowElevation();\n    }\n\n    @Override\n    public void setOutlineExcludePadding(boolean outlineExcludePadding) {\n        mLayoutHelper.setOutlineExcludePadding(outlineExcludePadding);\n    }\n\n    @Override\n    public void setShadowElevation(int elevation) {\n        mLayoutHelper.setShadowElevation(elevation);\n    }\n\n    @Override\n    public int getShadowElevation() {\n        return mLayoutHelper.getShadowElevation();\n    }\n\n    @Override\n    public void setShadowAlpha(float shadowAlpha) {\n        mLayoutHelper.setShadowAlpha(shadowAlpha);\n    }\n\n    @Override\n    public float getShadowAlpha() {\n        return mLayoutHelper.getShadowAlpha();\n    }\n\n    @Override\n    public void setShadowColor(int shadowColor) {\n        mLayoutHelper.setShadowColor(shadowColor);\n    }\n\n    @Override\n    public int getShadowColor() {\n        return mLayoutHelper.getShadowColor();\n    }\n\n    @Override\n    public void setOuterNormalColor(int color) {\n        mLayoutHelper.setOuterNormalColor(color);\n    }\n\n    @Override\n    public void updateBottomSeparatorColor(int color) {\n        mLayoutHelper.updateBottomSeparatorColor(color);\n    }\n\n    @Override\n    public void updateLeftSeparatorColor(int color) {\n        mLayoutHelper.updateLeftSeparatorColor(color);\n    }\n\n    @Override\n    public void updateRightSeparatorColor(int color) {\n        mLayoutHelper.updateRightSeparatorColor(color);\n    }\n\n    @Override\n    public void updateTopSeparatorColor(int color) {\n        mLayoutHelper.updateTopSeparatorColor(color);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        mLayoutHelper.drawDividers(canvas, getWidth(), getHeight());\n        mLayoutHelper.dispatchRoundBorderDraw(canvas);\n    }\n\n    @Override\n    public boolean hasBorder() {\n        return mLayoutHelper.hasBorder();\n    }\n\n    @Override\n    public boolean hasLeftSeparator() {\n        return mLayoutHelper.hasLeftSeparator();\n    }\n\n    @Override\n    public boolean hasTopSeparator() {\n        return mLayoutHelper.hasTopSeparator();\n    }\n\n    @Override\n    public boolean hasRightSeparator() {\n        return mLayoutHelper.hasRightSeparator();\n    }\n\n    @Override\n    public boolean hasBottomSeparator() {\n        return mLayoutHelper.hasBottomSeparator();\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/webview/QMUIBridgeWebViewClient.java",
    "content": "package com.qmuiteam.qmui.widget.webview;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.util.QMUILangHelper;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\npublic class QMUIBridgeWebViewClient extends QMUIWebViewClient {\n    public static final String QMUI_BRIDGE_HAS_MESSAGE = \"qmui://__QUEUE_MESSAGE__\";\n    public static final String QMUI_BRIDGE_JS = \"QMUIWebviewBridge.js\";\n\n    private QMUIWebViewBridgeHandler mWebViewBridgeHandler;\n    private boolean mNeedInjectLocalBridgeJs;\n\n    public QMUIBridgeWebViewClient(boolean needDispatchSafeAreaInset,\n                                   boolean disableVideoFullscreenBtnAlways,\n                                   @NonNull QMUIWebViewBridgeHandler bridgeHandler) {\n        this(needDispatchSafeAreaInset, disableVideoFullscreenBtnAlways, true, bridgeHandler);\n    }\n\n    public QMUIBridgeWebViewClient(boolean needDispatchSafeAreaInset,\n                                   boolean disableVideoFullscreenBtnAlways,\n                                   boolean needInjectLocalBridgeJs,\n                                   @NonNull QMUIWebViewBridgeHandler bridgeHandler) {\n        super(needDispatchSafeAreaInset, disableVideoFullscreenBtnAlways);\n        mNeedInjectLocalBridgeJs = needInjectLocalBridgeJs;\n        mWebViewBridgeHandler = bridgeHandler;\n    }\n\n    @Override\n    public final boolean shouldOverrideUrlLoading(WebView view, String url) {\n        if (url.startsWith(QMUI_BRIDGE_HAS_MESSAGE)) {\n            mWebViewBridgeHandler.fetchAndMessageFromJs();\n            return true;\n        }\n        return onShouldOverrideUrlLoading(view, url);\n    }\n\n    protected boolean onShouldOverrideUrlLoading(WebView view, String url){\n        return super.shouldOverrideUrlLoading(view, url);\n    }\n\n    @Override\n    public final boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            String url = request.getUrl().toString();\n            if (url.startsWith(QMUI_BRIDGE_HAS_MESSAGE)) {\n                mWebViewBridgeHandler.fetchAndMessageFromJs();\n                return true;\n            }\n        }\n\n        return onShouldOverrideUrlLoading(view, request);\n    }\n\n    @TargetApi(24)\n    protected boolean onShouldOverrideUrlLoading(WebView view, WebResourceRequest request){\n        return super.shouldOverrideUrlLoading(view, request);\n    }\n\n\n    @Override\n    public void onPageFinished(WebView view, String url) {\n        super.onPageFinished(view, url);\n        if(mNeedInjectLocalBridgeJs){\n            String bridgeScript = loadBridgeScript(view.getContext());\n            if (bridgeScript != null) {\n                view.evaluateJavascript(bridgeScript, null);\n                mWebViewBridgeHandler.onBridgeLoaded();\n            }\n        }else{\n            mWebViewBridgeHandler.onBridgeLoaded();\n        }\n\n    }\n\n    @Nullable\n    private static String loadBridgeScript(Context context) {\n        InputStream in = null;\n        try {\n            in = context.getAssets().open(QMUI_BRIDGE_JS);\n            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));\n            String line = bufferedReader.readLine();\n            StringBuilder sb = new StringBuilder();\n            while (line != null) {\n                sb.append(line);\n                sb.append(\"\\n\");\n                line = bufferedReader.readLine();\n            }\n\n            bufferedReader.close();\n            in.close();\n\n            return sb.toString();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            QMUILangHelper.close(in);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/webview/QMUIWebView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.webview;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class QMUIWebView extends WebView {\n\n    private static final String TAG = \"QMUIWebView\";\n    private static boolean sIsReflectionOccurError = false;\n\n    private Object mAwContents;\n    private Object mWebContents;\n    private Method mSetDisplayCutoutSafeAreaMethod;\n    private Rect mSafeAreaRectCache;\n\n    /**\n     * if true, the web content may be located under status bar\n     */\n    private boolean mNeedDispatchSafeAreaInset = false;\n    private Callback mCallback;\n    private List<OnScrollChangeListener> mOnScrollChangeListeners = new ArrayList<>();\n\n    public QMUIWebView(Context context) {\n        super(context);\n        init();\n    }\n\n    public QMUIWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public QMUIWebView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        removeJavascriptInterface(\"searchBoxJavaBridge_\");\n        removeJavascriptInterface(\"accessibility\");\n        removeJavascriptInterface(\"accessibilityTraversal\");\n        QMUIWindowInsetHelper.handleWindowInsets(this, WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout(), new QMUIWindowInsetHelper.InsetHandler() {\n            @Override\n            public void handleInset(View view, Insets insets) {\n                if (mNeedDispatchSafeAreaInset) {\n                    float density = QMUIDisplayHelper.getDensity(getContext());\n                    Rect rect = new Rect(\n                            (int) (insets.left / density + getExtraInsetLeft(density)),\n                            (int) (insets.top / density + getExtraInsetTop(density)),\n                            (int) (insets.right / density + getExtraInsetRight(density)),\n                            (int) (insets.bottom / density + getExtraInsetBottom(density))\n                    );\n                    setStyleDisplayCutoutSafeArea(rect);\n                }\n            }\n        }, true, false, false);\n    }\n\n    @Override\n    public void addJavascriptInterface(Object object, String name) {\n\n    }\n\n    @Deprecated\n    public void setCustomOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {\n        addCustomOnScrollChangeListener(onScrollChangeListener);\n    }\n\n    public void addCustomOnScrollChangeListener(OnScrollChangeListener listener) {\n        if (!mOnScrollChangeListeners.contains(listener)) {\n            mOnScrollChangeListeners.add(listener);\n        }\n    }\n\n    public void removeOnScrollChangeListener(OnScrollChangeListener listener) {\n        mOnScrollChangeListeners.remove(listener);\n    }\n\n    public void removeAllOnScrollChangeListener(){\n        mOnScrollChangeListeners.clear();\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        for (OnScrollChangeListener onScrollListener : mOnScrollChangeListeners) {\n            onScrollListener.onScrollChange(this, l, t, oldl, oldt);\n        }\n    }\n\n    @Override\n    public void setWebViewClient(WebViewClient client) {\n        if (client != null && !(client instanceof QMUIWebViewClient)) {\n            throw new IllegalArgumentException(\"must use the instance of QMUIWebViewClient\");\n        }\n        super.setWebViewClient(client);\n    }\n\n    @Override\n    public boolean dispatchKeyEvent(KeyEvent event) {\n        return super.dispatchKeyEvent(event);\n    }\n\n    public void setNeedDispatchSafeAreaInset(boolean needDispatchSafeAreaInset) {\n        if (mNeedDispatchSafeAreaInset != needDispatchSafeAreaInset) {\n            mNeedDispatchSafeAreaInset = needDispatchSafeAreaInset;\n            if (ViewCompat.isAttachedToWindow(this)) {\n                if (needDispatchSafeAreaInset) {\n                    ViewCompat.requestApplyInsets(this);\n                } else {\n                    // clear insets\n                    setStyleDisplayCutoutSafeArea(new Rect());\n                }\n            }\n        }\n    }\n\n    public boolean isNeedDispatchSafeAreaInset() {\n        return mNeedDispatchSafeAreaInset;\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n    }\n\n    private void doNotSupportChangeCssEnv() {\n        sIsReflectionOccurError = true;\n        if (mCallback != null) {\n            mCallback.onSureNotSupportChangeCssEnv();\n        }\n    }\n\n    boolean isNotSupportChangeCssEnv() {\n        return sIsReflectionOccurError;\n    }\n\n    protected int getExtraInsetTop(float density) {\n        return 0;\n    }\n\n    protected int getExtraInsetLeft(float density) {\n        return 0;\n    }\n\n    protected int getExtraInsetRight(float density) {\n        return 0;\n    }\n\n    protected int getExtraInsetBottom(float density) {\n        return 0;\n    }\n\n    @Override\n    public void destroy() {\n        mAwContents = null;\n        mWebContents = null;\n        mSetDisplayCutoutSafeAreaMethod = null;\n        stopLoading();\n        super.destroy();\n    }\n\n    private void setStyleDisplayCutoutSafeArea(@NonNull Rect rect) {\n        if (sIsReflectionOccurError || Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {\n            return;\n        }\n\n        if (rect == mSafeAreaRectCache) {\n            return;\n        }\n\n        if (mSafeAreaRectCache == null) {\n            mSafeAreaRectCache = new Rect(rect);\n        } else {\n            mSafeAreaRectCache.set(rect);\n        }\n\n        long start = System.currentTimeMillis();\n        if (mAwContents == null || mWebContents == null || mSetDisplayCutoutSafeAreaMethod == null) {\n            try {\n                Field providerField = WebView.class.getDeclaredField(\"mProvider\");\n                providerField.setAccessible(true);\n                Object provider = providerField.get(this);\n\n                mAwContents = getAwContentsFieldValueInProvider(provider);\n                if (mAwContents == null) {\n                    return;\n                }\n\n                mWebContents = getWebContentsFieldValueInAwContents(mAwContents);\n                if (mWebContents == null) {\n                    return;\n                }\n\n                mSetDisplayCutoutSafeAreaMethod = getSetDisplayCutoutSafeAreaMethodInWebContents(mWebContents);\n                if (mSetDisplayCutoutSafeAreaMethod == null) {\n                    // no such method, maybe the old version\n                    doNotSupportChangeCssEnv();\n                    return;\n                }\n            } catch (Exception e) {\n                doNotSupportChangeCssEnv();\n                Log.i(TAG, \"setStyleDisplayCutoutSafeArea error: \" + e);\n            }\n        }\n\n        try {\n            mSetDisplayCutoutSafeAreaMethod.setAccessible(true);\n            mSetDisplayCutoutSafeAreaMethod.invoke(mWebContents, rect);\n        } catch (Exception e) {\n            sIsReflectionOccurError = true;\n            Log.i(TAG, \"setStyleDisplayCutoutSafeArea error: \" + e);\n        }\n\n        Log.i(TAG, \"setDisplayCutoutSafeArea speed time: \" + (System.currentTimeMillis() - start));\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        ViewCompat.requestApplyInsets(this);\n    }\n\n    private Object getAwContentsFieldValueInProvider(Object provider) throws IllegalAccessException, NoSuchFieldException {\n        try {\n            Field awContentsField = provider.getClass().getDeclaredField(\"mAwContents\");\n            if (awContentsField != null) {\n                awContentsField.setAccessible(true);\n                return awContentsField.get(provider);\n            }\n        } catch (NoSuchFieldException ignored) {\n\n        }\n        // Unfortunately, the source code is ugly in some roms, so we can not reflect the field/method by name\n        for (Field field : provider.getClass().getDeclaredFields()) {\n            // 1. get field mAwContents\n            field.setAccessible(true);\n            Object awContents = field.get(provider);\n            if (awContents == null) {\n                continue;\n            }\n            if (awContents.getClass().getSimpleName().equals(\"AwContents\")) {\n                return awContents;\n            }\n        }\n        return null;\n    }\n\n    private Object getWebContentsFieldValueInAwContents(Object awContents) throws IllegalAccessException {\n        try {\n            Field webContentsField = awContents.getClass().getDeclaredField(\"mWebContents\");\n            if (webContentsField != null) {\n                webContentsField.setAccessible(true);\n                return webContentsField.get(awContents);\n            }\n        } catch (NoSuchFieldException ignored) {\n\n        }\n        // Unfortunately, the source code is ugly in some roms, so we can not reflect the field/method by name\n        for (Field innerField : awContents.getClass().getDeclaredFields()) {\n            innerField.setAccessible(true);\n            Object webContents = innerField.get(awContents);\n            if (webContents == null) {\n                continue;\n            }\n            if (webContents.getClass().getSimpleName().equals(\"WebContentsImpl\")) {\n                return webContents;\n            }\n        }\n        return null;\n    }\n\n    private Method getSetDisplayCutoutSafeAreaMethodInWebContents(Object webContents) {\n        try {\n           return webContents.getClass()\n                    .getDeclaredMethod(\"setDisplayCutoutSafeArea\", Rect.class);\n        } catch (NoSuchMethodException ignored) {\n\n        }\n        // Unfortunately, the source code is ugly in some roms, so we can not reflect the field/method by name\n        // not very safe in future\n        for (Method method : webContents.getClass().getDeclaredMethods()) {\n            if (method.getReturnType() == void.class && method.getParameterTypes().length == 1 &&\n                    method.getParameterTypes()[0] == Rect.class) {\n                return method;\n            }\n        }\n        return null;\n    }\n\n    public interface Callback {\n        void onSureNotSupportChangeCssEnv();\n    }\n\n    public interface OnScrollChangeListener {\n        /**\n         * Called when the scroll position of a view changes.\n         *\n         * @param webView    The view whose scroll position has changed.\n         * @param scrollX    Current horizontal scroll origin.\n         * @param scrollY    Current vertical scroll origin.\n         * @param oldScrollX Previous horizontal scroll origin.\n         * @param oldScrollY Previous vertical scroll origin.\n         */\n        void onScrollChange(WebView webView, int scrollX, int scrollY, int oldScrollX, int oldScrollY);\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/webview/QMUIWebViewBridgeHandler.java",
    "content": "package com.qmuiteam.qmui.widget.webview;\n\nimport android.util.Pair;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic abstract class QMUIWebViewBridgeHandler {\n    private static final String MESSAGE_JS_FETCH_SCRIPT = \"QMUIBridge._fetchQueueFromNative()\";\n    private static final String MESSAGE_JS_RESPONSE_SCRIPT = \"QMUIBridge._handleResponseFromNative($data$)\";\n    private static final String MESSAGE_PARAM_HOLDER = \"$data$\";\n    private static final String MESSAGE_CALLBACK_ID = \"callbackId\";\n    private static final String MESSAGE_DATA = \"data\";\n    private static final String MESSAGE_INNER_CMD_NAME = \"__cmd__\";\n    private static final String MESSAGE_CMD_GET_SUPPORTED_CMD_LIST = \"getSupportedCmdList\";\n\n    private List<Pair<String, ValueCallback<String>>> mStartupMessageList = new ArrayList<>();\n    private WebView mWebView;\n\n    public QMUIWebViewBridgeHandler(@NonNull WebView webView) {\n        mWebView = webView;\n    }\n\n    public final void evaluateBridgeScript(String script, ValueCallback<String> resultCallback) {\n        if (mStartupMessageList != null) {\n            mStartupMessageList.add(new Pair<>(script, resultCallback));\n        } else {\n            mWebView.evaluateJavascript(script, resultCallback);\n        }\n    }\n\n    void onBridgeLoaded() {\n        if (mStartupMessageList != null) {\n            for (Pair<String, ValueCallback<String>> message : mStartupMessageList) {\n                mWebView.evaluateJavascript(message.first, message.second);\n            }\n            mStartupMessageList = null;\n        }\n    }\n\n\n    void fetchAndMessageFromJs() {\n        mWebView.evaluateJavascript(MESSAGE_JS_FETCH_SCRIPT, new ValueCallback<String>() {\n            @Override\n            public void onReceiveValue(String value) {\n                String unescaped = unescape(value);\n                if (unescaped != null) {\n                    try {\n                        JSONArray array = new JSONArray(unescaped);\n                        for (int i = 0; i < array.length(); i++) {\n                            JSONObject message = array.getJSONObject(i);\n                            String callbackId = message.getString(MESSAGE_CALLBACK_ID);\n                            String msgDataOrigin = message.getString(MESSAGE_DATA);\n                            MessageFinishCallback callback = new MessageFinishCallback(callbackId) {\n                                @Override\n                                public void finish(Object data) {\n                                    try{\n                                        JSONObject response = new JSONObject();\n                                        response.put(MESSAGE_CALLBACK_ID, getCallbackId());\n                                        response.put(MESSAGE_DATA, data);\n                                        String script = MESSAGE_JS_RESPONSE_SCRIPT.replace(MESSAGE_PARAM_HOLDER, response.toString());\n                                        mWebView.evaluateJavascript(script, null);\n                                    }catch (Throwable ignore){\n\n                                    }\n                                }\n                            };\n                            try{\n                                JSONObject msgData = new JSONObject(msgDataOrigin);\n                                String cmdName = msgData.getString(MESSAGE_INNER_CMD_NAME);\n                                handleInnerMessage(cmdName, msgData, callback);\n                            }catch (Throwable e){\n                                handleMessage(msgDataOrigin, callback);\n                            }\n                        }\n                    } catch (JSONException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        });\n    }\n\n\n    private void handleInnerMessage(String cmdName, JSONObject jsonObject, MessageFinishCallback callback){\n        if(MESSAGE_CMD_GET_SUPPORTED_CMD_LIST.equals(cmdName)){\n            callback.finish(new JSONArray(getSupportedCmdList()));\n        }else{\n            throw new RuntimeException(\"not a inner api message. fallback to custom message\");\n        }\n    }\n\n    protected abstract List<String> getSupportedCmdList();\n\n    protected abstract void handleMessage(String message, MessageFinishCallback callback);\n\n    @Nullable\n    public static String unescape(@Nullable String value) {\n        if (value == null || value.isEmpty()) {\n            return null;\n        }\n        String ret = value.substring(1, value.length() - 1)\n                .replace(\"\\\\\\\\\", \"\\\\\")\n                .replace(\"\\\\\\\"\", \"\\\"\");\n        if (\"null\".equals(ret)) {\n            return null;\n        }\n        return ret;\n    }\n\n    @NonNull\n    public static String escape(@Nullable String value) {\n        if (value == null || value.isEmpty()) {\n            return \"\\\"null\\\"\";\n        }\n        String ret = value\n                .replace(\"\\\\\", \"\\\\\\\\\")\n                .replace(\"\\\"\", \"\\\\\\\"\");\n        return \"\\\"\" + ret + \"\\\"\";\n    }\n\n\n    public abstract class MessageFinishCallback{\n\n        private final String mCallbackId;\n\n        public MessageFinishCallback(String callbackId){\n            mCallbackId = callbackId;\n        }\n\n        public String getCallbackId() {\n            return mCallbackId;\n        }\n\n        public abstract void finish(Object data);\n    }\n\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/webview/QMUIWebViewClient.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.webview;\n\nimport android.graphics.Bitmap;\nimport android.os.SystemClock;\nimport android.view.KeyCharacterMap;\nimport android.view.KeyEvent;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\npublic class QMUIWebViewClient extends WebViewClient {\n\n    public static final int JS_FAKE_KEY_CODE_EVENT = 112; // F1\n\n    private boolean mNeedDispatchSafeAreaInset;\n    private boolean mDisableVideoFullscreenBtnAlways;\n    private boolean mIsPageFinished = false;\n\n    public QMUIWebViewClient(boolean needDispatchSafeAreaInset, boolean disableVideoFullscreenBtnAlways) {\n        mNeedDispatchSafeAreaInset = needDispatchSafeAreaInset;\n        mDisableVideoFullscreenBtnAlways = disableVideoFullscreenBtnAlways;\n    }\n\n\n    public void setNeedDispatchSafeAreaInset(QMUIWebView webView) {\n        if (!mNeedDispatchSafeAreaInset) {\n            mNeedDispatchSafeAreaInset = true;\n            if (mIsPageFinished) {\n                dispatchFullscreenRequestAction(webView);\n            }\n        }\n    }\n\n    @Override\n    public void onPageStarted(WebView view, String url, @Nullable Bitmap favicon) {\n        mIsPageFinished = false;\n        super.onPageStarted(view, url, favicon);\n    }\n\n\n    @Override\n    public void onPageFinished(final WebView view, String url) {\n        super.onPageFinished(view, url);\n        mIsPageFinished = true;\n        if (mDisableVideoFullscreenBtnAlways) {\n            runJsCode(view, getJsCodeForDisableVideoFullscreenBtn(), null);\n        }\n        if (mNeedDispatchSafeAreaInset && view instanceof QMUIWebView) {\n            dispatchFullscreenRequestAction((QMUIWebView) view);\n        }\n    }\n\n    private String getJsCodeForDisableVideoFullscreenBtn() {\n        return \"(function(){\\n\" +\n                // disable fullscreen btn on video\n                \"   var head = document.getElementsByTagName('head')[0];\\n\" +\n                \"   var style = document.createElement('style');\\n\" +\n                \"   style.type = 'text/css';\" +\n                \"   style.innerHTML = 'video::-webkit-media-controls-fullscreen-button{display: none !important;}'\\n\" +\n                \"   head.appendChild(style);\\n\" +\n                \"})()\";\n    }\n\n    private String getJsCodeForFullscreenHtml() {\n        return \"(function(){\\n\" +\n                \"   document.body.addEventListener('keydown', function(e){\\n\" +\n                \"        if(e.keyCode == \" + JS_FAKE_KEY_CODE_EVENT + \"){\\n\" +\n                \"             var html = document.documentElement;\\n\" +\n                \"             var requestFullscreen = html.requestFullscreen || html.webkitRequestFullscreen;\\n\" +\n                \"             requestFullscreen.call(html);\\n\" +\n                \"        }\\n\" +\n                \"    })\\n\" +\n                \"})()\";\n    }\n\n    private void dispatchFullscreenRequestAction(final QMUIWebView webView) {\n        boolean sureNotSupportModifyCssEnv = webView.isNotSupportChangeCssEnv();\n        if (sureNotSupportModifyCssEnv) {\n            return;\n        }\n\n        if (!mDisableVideoFullscreenBtnAlways) {\n            runJsCode(webView, getJsCodeForDisableVideoFullscreenBtn(), null);\n        }\n        runJsCode(webView, getJsCodeForFullscreenHtml(), new Runnable() {\n            @Override\n            public void run() {\n                dispatchFullscreenRequestEvent(webView);\n            }\n        });\n    }\n\n    private void dispatchFullscreenRequestEvent(WebView webView) {\n        KeyEvent keyEvent = new KeyEvent(SystemClock.uptimeMillis(),\n                SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_F1,\n                0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0);\n        webView.dispatchKeyEvent(keyEvent);\n    }\n\n    private void runJsCode(WebView webView, @NonNull String jsCode, @Nullable final Runnable finishAction) {\n        if (finishAction == null) {\n            webView.evaluateJavascript(jsCode, null);\n        } else {\n            webView.evaluateJavascript(jsCode, new ValueCallback<String>() {\n                @Override\n                public void onReceiveValue(String value) {\n                    finishAction.run();\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/java/com/qmuiteam/qmui/widget/webview/QMUIWebViewContainer.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.widget.webview;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\n\npublic class QMUIWebViewContainer extends QMUIFrameLayout {\n\n    private QMUIWebView mWebView;\n    private QMUIWebView.OnScrollChangeListener mOnScrollChangeListener;\n\n    public QMUIWebViewContainer(Context context) {\n        super(context);\n    }\n\n    public QMUIWebViewContainer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n\n    public void addWebView(@NonNull QMUIWebView webView, boolean needDispatchSafeAreaInset) {\n        mWebView = webView;\n        mWebView.setNeedDispatchSafeAreaInset(needDispatchSafeAreaInset);\n        mWebView.addCustomOnScrollChangeListener(new QMUIWebView.OnScrollChangeListener() {\n            @Override\n            public void onScrollChange(WebView webView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n                if (mOnScrollChangeListener != null) {\n                    mOnScrollChangeListener.onScrollChange(webView, scrollX, scrollY, oldScrollX, oldScrollY);\n                }\n            }\n        });\n        addView(mWebView, getWebViewLayoutParams());\n        QMUIWindowInsetHelper.handleWindowInsets(this, WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.displayCutout());\n    }\n\n    protected FrameLayout.LayoutParams getWebViewLayoutParams() {\n        return new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n    }\n\n\n    public void setNeedDispatchSafeAreaInset(boolean needDispatchSafeAreaInset) {\n        if (mWebView != null) {\n            mWebView.setNeedDispatchSafeAreaInset(needDispatchSafeAreaInset);\n        }\n    }\n\n    public void destroy() {\n        removeView(mWebView);\n        removeAllViews();\n        mWebView.setWebChromeClient(null);\n        mWebView.setWebViewClient(null);\n        mWebView.destroy();\n    }\n\n\n    public void setCustomOnScrollChangeListener(QMUIWebView.OnScrollChangeListener onScrollChangeListener) {\n        mOnScrollChangeListener = onScrollChangeListener;\n    }\n}\n"
  },
  {
    "path": "qmui/src/main/res/anim/decelerate_factor_interpolator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<decelerateInterpolator\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:factor=\"1.4\" />"
  },
  {
    "path": "qmui/src/main/res/anim/decelerate_low_factor_interpolator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<decelerateInterpolator\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:factor=\"1.1\" />"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"50%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_bottomleft_to_topright.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"0%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_bottomright_to_topleft.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"100%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"50%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_topleft_to_bottomright.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"0%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/grow_from_topright_to_bottomleft.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"100%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>"
  },
  {
    "path": "qmui/src/main/res/anim/scale_in_center.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"0.3\" android:toXScale=\"1.0\"\n\t\tandroid:fromYScale=\"0.3\" android:toYScale=\"1.0\"\n\t\tandroid:pivotX=\"50%\" android:pivotY=\"50%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/decelerate_interpolator\"\n\t\tandroid:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/scale_out_center.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <alpha\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromAlpha=\"1.0\"\n        android:interpolator=\"@android:anim/accelerate_interpolator\"\n        android:toAlpha=\"0.0\" />\n    <scale\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"0.3\"\n        android:toYScale=\"0.3\" />\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"50%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_bottomleft_to_topright.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"100%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_bottomright_to_topleft.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"0%\" android:pivotY=\"0%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"50%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_topleft_to_bottomright.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"100%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>"
  },
  {
    "path": "qmui/src/main/res/anim/shrink_from_topright_to_bottomleft.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<scale\n\t\tandroid:fromXScale=\"1.0\" android:toXScale=\"0.3\"\n\t\tandroid:fromYScale=\"1.0\" android:toYScale=\"0.3\"\n\t\tandroid:pivotX=\"0%\" android:pivotY=\"100%\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n\t<alpha\n\t\tandroid:interpolator=\"@android:anim/accelerate_interpolator\"\n\t\tandroid:fromAlpha=\"1.0\" android:toAlpha=\"0.0\"\n\t\tandroid:duration=\"@android:integer/config_shortAnimTime\"\n\t/>\n</set>\n"
  },
  {
    "path": "qmui/src/main/res/color/qmui_btn_blue_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/btn_filled_blue_bg_disabled\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/btn_filled_blue_bg_pressed\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/btn_filled_blue_bg_normal\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_btn_blue_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/btn_ghost_blue_border_disabled\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/btn_ghost_blue_border_pressed\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/btn_ghost_blue_border_normal\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_btn_blue_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/btn_ghost_blue_text_disabled\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/btn_ghost_blue_text_pressed\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/btn_ghost_blue_text_normal\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_s_link_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/qmui_config_color_50_blue\"/>\n    <item android:color=\"@color/qmui_config_color_blue\"/>\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_s_list_item_text_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_gray_3\" android:state_enabled=\"false\"/>\n    <item android:color=\"@color/qmui_config_color_gray_1\"/>\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_s_switch_text_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_50_pure_black\" android:state_selected=\"true\" />\n    <item android:color=\"?attr/qmui_config_color_gray_3\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_s_transparent.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_transparent\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/qmui_config_color_transparent\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/qmui_config_color_transparent\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/color/qmui_topbar_text_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- Topbar的文字按钮颜色 -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_enabled=\"false\" android:color=\"#801B88EE\"/>\n    <item android:state_pressed=\"true\" android:color=\"#801B88EE\"/>\n    <item android:color=\"@color/qmui_config_color_blue\"/>\n\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_divider_bottom_bitmap.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:gravity=\"fill_horizontal|bottom\"\n       android:src=\"@drawable/qmui_divider\"\n       android:tint=\"@color/qmui_drawable_color_list_separator\" />\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_divider_top_bitmap.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:gravity=\"fill_horizontal|top\"\n    android:src=\"@drawable/qmui_divider\"\n    android:tint=\"@color/qmui_drawable_color_list_separator\" />\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_popup_close.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <group\n      android:name=\"iconGroup\"\n      android:pivotX=\"12\"\n      android:pivotY=\"12\"\n      android:rotation=\"0\">\n\n    <path\n        android:name=\"close\"\n        android:pathData=\"M10.95,12.05L11.95,13.05L6.0303,19.0303L4.9697,17.9697L10.95,12.05ZM4.9697,6.0303L6.0303,4.9697L19.0303,17.9697L17.9697,19.0303L4.9697,6.0303ZM17.9697,4.9697L19.0303,6.0303L13.05,11.95L12.05,10.95L17.9697,4.9697Z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:strokeColor=\"#00000000\"/>\n\n  </group>\n\n</vector>\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_popup_close_with_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"oval\">\n            <size\n                android:width=\"48dp\"\n                android:height=\"48dp\" />\n            <solid android:color=\"#0E0E0E\" />\n        </shape>\n    </item>\n    <item\n        android:drawable=\"@drawable/qmui_icon_popup_close\"\n        android:gravity=\"center\" />\n\n</layer-list>\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_pull_down.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"9dp\"\n    android:height=\"12dp\"\n    android:viewportWidth=\"9\"\n    android:viewportHeight=\"12\">\n  <path\n      android:pathData=\"M5.5,11l0,-11l-1.5,-0l0,11z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M4.7587,9.5304l2.7413,-2.5304l1.0174,1.1022l-3.7587,3.4696l-3.7587,-3.4696l1.0174,-1.1022z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_quick_action_more_arrow_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<rotate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/qmui_icon_quick_action_more_arrow_right\"\n    android:fromDegrees=\"180\"\n    android:pivotX=\"50%\"\n    android:pivotY=\"50%\"\n    android:toDegrees=\"180\" />\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_quick_action_more_arrow_right.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"12dp\"\n        android:height=\"12dp\"\n        android:viewportWidth=\"12.0\"\n        android:viewportHeight=\"12.0\">\n    <path\n        android:pathData=\"M3.936,9.506l1.129,0.988l3.932,-4.494l-3.932,-4.494l-1.129,0.988l3.068,3.506z\"\n        android:strokeColor=\"#00000000\"\n        android:fillColor=\"#FFFFFF\"\n        android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_icon_topbar_back.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"24dp\"\n        android:height=\"24dp\"\n        android:viewportWidth=\"24.0\"\n        android:viewportHeight=\"24.0\">\n    <path\n        android:pathData=\"M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z\"\n        android:fillColor=\"#ffffff\"/>\n</vector>\n"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_checkbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- 单选类型的对话框的单选Drawable -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/qmui_icon_checkbox_checked\" android:state_selected=\"true\"/>\n    <item android:drawable=\"@drawable/qmui_icon_checkbox_checked\" android:state_checked=\"true\"/>\n    <item android:drawable=\"@drawable/qmui_icon_checkbox_normal\"/>\n\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_icon_switch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/qmui_icon_switch_checked\" android:state_checked=\"true\"/>\n    <item android:drawable=\"@drawable/qmui_icon_switch_normal\"/>\n\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_list_item_bg_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_pressed\" android:state_pressed=\"true\"/>\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_list_item_bg_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_drawable_color_list_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/qmui_config_color_white\"/>\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_switch_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/qmui_switch_thumb\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/qmui_switch_thumb\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/qmui_switch_thumb_checked\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/qmui_switch_thumb\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_s_switch_track.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/qmui_switch_track\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/qmui_switch_track\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/qmui_switch_track_checked\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/qmui_switch_track\" />\n</selector>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_switch_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\"\n    >\n    <size\n        android:width=\"@dimen/qmui_switch_size\"\n        android:height=\"@dimen/qmui_switch_size\"\n        />\n    <solid android:color=\"?attr/qmui_config_color_gray_3\" />\n</shape>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_switch_thumb_checked.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"oval\"\n    >\n    <size\n        android:width=\"@dimen/qmui_switch_size\"\n        android:height=\"@dimen/qmui_switch_size\"\n        />\n    <solid android:color=\"?attr/qmui_config_color_link\" />\n</shape>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_switch_track.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\"\n    >\n    <corners android:radius=\"12dp\" />\n    <size\n        android:width=\"@dimen/qmui_switch_size\"\n        android:height=\"@dimen/qmui_switch_size\"\n        />\n    <solid android:color=\"?attr/qmui_config_color_gray_3\" />\n    <stroke\n        android:width=\"4dp\"\n        android:color=\"#00000000\"\n        />\n</shape>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_switch_track_checked.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\"\n    >\n    <corners android:radius=\"12dp\" />\n    <size\n        android:width=\"@dimen/qmui_switch_size\"\n        android:height=\"@dimen/qmui_switch_size\"\n        />\n    <solid android:color=\"@color/qmui_config_color_50_pure_black\" />\n    <stroke\n        android:width=\"4dp\"\n        android:color=\"#00000000\"\n        />\n</shape>"
  },
  {
    "path": "qmui/src/main/res/drawable/qmui_tips_point.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\"oval\">\n    <solid android:color=\"@color/qmui_config_color_red\" />\n</shape>"
  },
  {
    "path": "qmui/src/main/res/drawable-v21/qmui_s_list_item_bg_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"@color/qmui_drawable_color_list_pressed\">\n    <item android:drawable=\"@color/qmui_config_color_white\" />\n</ripple>"
  },
  {
    "path": "qmui/src/main/res/layout/qmui_bottom_sheet_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <androidx.coordinatorlayout.widget.CoordinatorLayout\n      android:id=\"@+id/coordinator\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:fitsSystemWindows=\"true\">\n\n    <View\n        android:id=\"@+id/touch_outside\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:importantForAccessibility=\"no\"\n        android:soundEffectsEnabled=\"false\"\n        tools:ignore=\"UnusedAttribute\"/>\n\n    <com.qmuiteam.qmui.widget.dialog.QMUIBottomSheetRootLayout\n        android:id=\"@+id/bottom_sheet\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal|top\"/>\n\n  </androidx.coordinatorlayout.widget.CoordinatorLayout>\n\n</FrameLayout>\n"
  },
  {
    "path": "qmui/src/main/res/layout/qmui_common_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/group_list_item_imageView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:adjustViewBounds=\"true\"\n        android:contentDescription=\"@null\"\n        android:scaleType=\"fitCenter\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:qmui_skin_tint_color=\"?attr/qmui_skin_support_common_list_icon_tint_color\" />\n\n    <FrameLayout\n        android:id=\"@+id/group_list_item_accessoryView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n\n    <TextView\n        android:id=\"@+id/group_list_item_textView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"?attr/qmui_common_list_item_icon_margin_right\"\n        android:layout_marginRight=\"?attr/qmui_common_list_item_accessory_margin_left\"\n        android:ellipsize=\"end\"\n        android:includeFontPadding=\"false\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        android:lineSpacingExtra=\"?attr/qmui_common_list_item_title_line_space\"\n        android:textColor=\"?attr/qmui_skin_support_common_list_title_color\"\n        android:textSize=\"?attr/qmui_common_list_item_title_h_text_size\"\n        app:layout_constrainedWidth=\"true\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toRightOf=\"@+id/group_list_item_imageView\"\n        app:layout_constraintRight_toLeftOf=\"@+id/group_list_item_accessoryView\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_goneMarginLeft=\"0dp\"\n        app:layout_goneMarginRight=\"0dp\"\n        app:layout_constraintHorizontal_bias=\"0\"\n        app:qmui_skin_text_color=\"?attr/qmui_skin_support_common_list_title_color\"/>\n\n\n    <TextView\n        android:id=\"@+id/group_list_item_detailTextView\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"?attr/qmui_common_list_item_detail_h_margin_with_title\"\n        android:layout_marginRight=\"?attr/qmui_common_list_item_accessory_margin_left\"\n        android:includeFontPadding=\"false\"\n        android:lineSpacingExtra=\"?attr/qmui_common_list_item_detail_line_space\"\n        android:textColor=\"?attr/qmui_skin_support_common_list_detail_color\"\n        android:textSize=\"?attr/qmui_common_list_item_detail_h_text_size\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintHorizontal_bias=\"1\"\n        app:layout_constraintWidth_default=\"wrap\"\n        app:layout_constraintLeft_toRightOf=\"@+id/group_list_item_textView\"\n        app:layout_constraintRight_toLeftOf=\"@+id/group_list_item_accessoryView\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_goneMarginRight=\"0dp\"\n        app:qmui_skin_text_color=\"?attr/qmui_skin_support_common_list_detail_color\"/>\n\n\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/group_list_item_tips_new\"\n        android:contentDescription=\"Update tips\"\n        android:visibility=\"gone\"\n        style=\"?attr/QMUITipNewStyle\"\n        app:qmui_skin_src=\"?attr/qmui_skin_support_common_list_new_drawable\"/>\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/group_list_item_tips_dot\"\n        style=\"?attr/QMUITipPointStyle\"\n        android:contentDescription=\"Red dot\"\n        android:visibility=\"gone\"\n        app:qmui_skin_bg_tint_color=\"?attr/qmui_skin_support_common_list_red_point_tint_color\"/>\n\n</merge>"
  },
  {
    "path": "qmui/src/main/res/layout/qmui_empty_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.qmuiteam.qmui.widget.QMUILoadingView\n        android:id=\"@+id/empty_view_loading\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:color=\"?attr/qmui_skin_support_empty_view_loading_color\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@+id/empty_view_title\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        app:qmui_loading_view_size=\"?attr/qmui_empty_view_loading_size\"\n        app:qmui_skin_tint_color=\"?attr/qmui_skin_support_empty_view_loading_color\" />\n\n    <com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView\n        android:id=\"@+id/empty_view_title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"?attr/qmui_empty_view_title_margin_hor\"\n        android:layout_marginRight=\"?attr/qmui_empty_view_title_margin_hor\"\n        android:layout_marginTop=\"?attr/qmui_empty_view_title_normal_margin_top\"\n        android:gravity=\"center_horizontal\"\n        android:textColor=\"?attr/qmui_skin_support_empty_view_title_color\"\n        android:textSize=\"?attr/qmui_empty_view_title_text_size\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@+id/empty_view_detail\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/empty_view_loading\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        app:layout_goneMarginTop=\"0dp\"\n        app:qmui_skin_text_color=\"?attr/qmui_skin_support_empty_view_title_color\" />\n\n\n    <com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView\n        android:id=\"@+id/empty_view_detail\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"?attr/qmui_empty_view_sub_title_margin_hor\"\n        android:layout_marginRight=\"?attr/qmui_empty_view_sub_title_margin_hor\"\n        android:layout_marginTop=\"?attr/qmui_empty_view_sub_title_normal_margin_top\"\n        android:gravity=\"center_horizontal\"\n        android:textColor=\"?attr/qmui_skin_support_empty_view_sub_title_color\"\n        android:textSize=\"?attr/qmui_empty_view_sub_title_text_size\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@+id/empty_view_button\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/empty_view_title\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        app:qmui_skin_text_color=\"?attr/qmui_skin_support_empty_view_sub_title_color\" />\n\n\n    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n        android:id=\"@+id/empty_view_button\"\n        style=\"@style/QMUI.RoundButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"?attr/qmui_empty_view_btn_height\"\n        android:layout_marginLeft=\"?attr/qmui_empty_view_btn_margin_hor\"\n        android:layout_marginRight=\"?attr/qmui_empty_view_btn_margin_hor\"\n        android:layout_marginTop=\"?attr/qmui_empty_view_btn_normal_margin_top\"\n        android:textColor=\"?attr/qmui_skin_support_empty_view_btn_text_color\"\n        android:textSize=\"?attr/qmui_empty_view_btn_text_size\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/empty_view_detail\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        app:qmui_backgroundColor=\"?attr/qmui_skin_support_empty_view_btn_bg_color\"\n        app:qmui_borderColor=\"?attr/qmui_skin_support_empty_view_btn_border_color\"\n        app:qmui_skin_background=\"?attr/qmui_skin_support_empty_view_btn_bg_color\"\n        app:qmui_skin_border=\"?attr/qmui_skin_support_empty_view_btn_border_color\"\n        app:qmui_skin_text_color=\"?attr/qmui_skin_support_empty_view_btn_text_color\" />\n\n</merge>"
  },
  {
    "path": "qmui/src/main/res/layout/qmui_group_list_section_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/group_list_section_header_textView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/qmui_group_list_section_header_footer_text_size\"\n        android:textColor=\"?attr/qmui_config_color_gray_3\"\n        android:gravity=\"left\" />\n</merge>"
  },
  {
    "path": "qmui/src/main/res/values/config_colors.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!-- button -->\n    <color name=\"btn_ghost_blue_border_normal\">@color/qmui_config_color_blue</color>\n    <color name=\"btn_ghost_blue_border_pressed\">#801B88EE</color>\n    <color name=\"btn_ghost_blue_border_disabled\">#801B88EE</color>\n\n    <color name=\"btn_ghost_blue_text_normal\">@color/qmui_config_color_blue</color>\n    <color name=\"btn_ghost_blue_text_pressed\">#801B88EE</color>\n    <color name=\"btn_ghost_blue_text_disabled\">#801B88EE</color>\n\n    <color name=\"btn_filled_blue_bg_normal\">#416f96</color>\n    <color name=\"btn_filled_blue_bg_pressed\">#243f55</color>\n    <color name=\"btn_filled_blue_bg_disabled\">#80416f96</color>\n\n\n</resources>\n"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <attr name=\"qmui_backgroundColor\" format=\"color\"/>\n    <attr name=\"qmui_borderColor\" format=\"color\"/>\n    <attr name=\"qmui_borderWidth\" format=\"dimension\"/>\n    <attr name=\"qmui_linkColor\" format=\"color\"/>\n    <attr name=\"qmui_orientation\" format=\"enum\">\n        <enum name=\"horizontal\" value=\"0\"/>\n        <enum name=\"vertical\" value=\"1\"/>\n    </attr>\n    <attr name=\"qmui_title\" format=\"string\"/>\n\n    <!--**************** QMUITabSegment ******************-->\n    <declare-styleable name=\"QMUITabSegment\">\n        <attr name=\"qmui_tab_indicator_height\" format=\"dimension\"/>\n        <attr name=\"qmui_tab_has_indicator\" format=\"boolean\"/>\n        <attr name=\"qmui_tab_indicator_top\" format=\"boolean\"/>\n        <attr name=\"qmui_tab_indicator_with_follow_content\" format=\"boolean\"/>\n        <attr name=\"android:textSize\"/>\n        <attr name=\"qmui_tab_normal_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_tab_selected_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_tab_mode\" format=\"enum\">\n            <enum name=\"scrollable\" value=\"0\"/>\n            <enum name=\"fixed\" value=\"1\"/>\n        </attr>\n        <attr name=\"qmui_tab_space\" format=\"dimension\"/>\n        <attr name=\"qmui_tab_icon_position\" format=\"enum\">\n            <enum name=\"left\" value=\"0\"/>\n            <enum name=\"top\" value=\"1\"/>\n            <enum name=\"right\" value=\"2\"/>\n            <enum name=\"bottom\" value=\"3\"/>\n        </attr>\n        <attr name=\"qmui_tab_select_no_animation\" format=\"boolean\" />\n    </declare-styleable>\n    <attr name=\"QMUITabSegmentStyle\" format=\"reference\"/>\n\n    <!--************** QMUIGroupListView **************-->\n\n    <attr name=\"QMUICommonListItemViewStyle\" format=\"reference\"/>\n    <attr name=\"qmui_common_list_item_chevron\" format=\"reference\"/>\n    <attr name=\"qmui_common_list_item_switch\" format=\"reference\"/>\n    <declare-styleable name=\"QMUICommonListItemView\">\n        <attr name=\"qmui_orientation\"/>\n        <attr name=\"qmui_accessory_type\" format=\"enum\">\n            <enum name=\"none\" value=\"0\"/>\n            <enum name=\"chevron\" value=\"1\"/>\n            <enum name=\"switcher\" value=\"2\"/>\n            <enum name=\"custom\" value=\"3\"/>\n        </attr>\n        <attr name=\"qmui_common_list_title_color\" format=\"reference|color\"/>\n        <attr name=\"qmui_common_list_detail_color\" format=\"reference|color\"/>\n    </declare-styleable>\n    <attr name=\"QMUIGroupListSectionViewStyle\" format=\"reference\"/>\n\n\n    <!--************ 更新小红点与 new 的样式配置 ***********-->\n    <attr name=\"QMUITipPointStyle\" format=\"reference\"/>\n    <attr name=\"QMUITipNewStyle\" format=\"reference\"/>\n\n    <!--************ QMUILoading ***********-->\n    <declare-styleable name=\"QMUILoadingView\">\n        <attr name=\"qmui_loading_view_size\" format=\"dimension\"/>\n        <attr name=\"android:color\"/>\n    </declare-styleable>\n    <attr name=\"QMUILoadingStyle\" format=\"reference\"/>\n\n    <!--************ QMUITopBar ***********-->\n    <declare-styleable name=\"QMUITopBar\">\n        <attr name=\"android:ellipsize\"/>\n        <attr name=\"qmui_topbar_title_gravity\" format=\"enum\">\n            <enum name=\"left_center\" value=\"19\"/>\n            <enum name=\"center\" value=\"17\"/>\n        </attr>\n        <attr name=\"qmui_topbar_left_back_drawable_id\" format=\"reference\"/>\n        <attr name=\"qmui_topbar_left_back_width\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_clear_left_padding_when_add_left_back_view\" format=\"boolean\"/>\n        <attr name=\"qmui_topbar_title_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_subtitle_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_title_color\" format=\"color\"/>\n        <attr name=\"qmui_topbar_title_bold\" format=\"boolean\"/>\n        <attr name=\"qmui_topbar_title_text_size_with_subtitle\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_subtitle_bold\" format=\"boolean\"/>\n        <attr name=\"qmui_topbar_subtitle_color\" format=\"color\"/>\n        <attr name=\"qmui_topbar_title_margin_horizontal_when_no_btn_aside\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_title_container_padding_horizontal\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_image_btn_width\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_image_btn_height\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_text_btn_padding_horizontal\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_text_btn_color_state_list\" format=\"reference\"/>\n        <attr name=\"qmui_topbar_text_btn_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_topbar_text_btn_bold\" format=\"boolean\"/>\n    </declare-styleable>\n    <attr name=\"QMUITopBarStyle\" format=\"reference\"/>\n\n    <!--**************** QMUIPullRefreshLayout ******************-->\n    <declare-styleable name=\"QMUIPullRefreshLayout\">\n        <!-- RefreshView的初始offset -->\n        <attr name=\"qmui_refresh_init_offset\" format=\"dimension\"/>\n        <!-- 刷新时RefreshView的offset -->\n        <attr name=\"qmui_refresh_end_offset\" format=\"dimension\"/>\n        <!-- TargetView(ListView或者ScrollView等)的初始位置 -->\n        <attr name=\"qmui_target_init_offset\" format=\"dimension\"/>\n        <!-- 刷新时TargetView(ListView或者ScrollView等)的位置-->\n        <attr name=\"qmui_target_refresh_offset\" format=\"dimension\"/>\n        <!-- 是否自动根据RefreshView的高度计算RefreshView的初始位置-->\n        <attr name=\"qmui_auto_calculate_refresh_init_offset\" format=\"boolean\"/>\n        <!-- 是否自动根据TargetView在刷新时的位置计算RefreshView的结束位置-->\n        <attr name=\"qmui_auto_calculate_refresh_end_offset\" format=\"boolean\"/>\n        <!-- TargetView的刷新位置与RefreshView高度相等-->\n        <attr name=\"qmui_equal_target_refresh_offset_to_refresh_view_height\" format=\"boolean\"/>\n    </declare-styleable>\n    <attr name=\"QMUIPullRefreshLayoutStyle\" format=\"reference\"/>\n\n    <!--************** QMUIRadiusImageView ****************-->\n    <attr name=\"qmui_border_width\" format=\"dimension\"/>\n    <attr name=\"qmui_border_color\" format=\"color\"/>\n    <attr name=\"qmui_selected_mask_color\" format=\"color\"/>\n    <attr name=\"qmui_selected_border_color\" format=\"color\"/>\n    <attr name=\"qmui_selected_border_width\" format=\"dimension\"/>\n    <attr name=\"qmui_corner_radius\" format=\"dimension\"/>\n    <attr name=\"qmui_is_oval\" format=\"boolean\"/>\n    <attr name=\"qmui_is_circle\" format=\"boolean\"/>\n    <attr name=\"qmui_is_touch_select_mode_enabled\" format=\"boolean\"/>\n    <declare-styleable name=\"QMUIRadiusImageView\">\n        <attr name=\"qmui_border_width\"/>\n        <attr name=\"qmui_border_color\"/>\n        <attr name=\"qmui_selected_mask_color\"/>\n        <attr name=\"qmui_selected_border_color\"/>\n        <attr name=\"qmui_selected_border_width\"/>\n        <attr name=\"qmui_corner_radius\"/>\n        <attr name=\"qmui_is_oval\"/>\n        <attr name=\"qmui_is_circle\"/>\n        <attr name=\"qmui_is_touch_select_mode_enabled\"/>\n    </declare-styleable>\n    <declare-styleable name=\"QMUIRadiusImageView2\">\n        <attr name=\"qmui_border_width\"/>\n        <attr name=\"qmui_border_color\"/>\n        <attr name=\"qmui_selected_mask_color\"/>\n        <attr name=\"qmui_selected_border_color\"/>\n        <attr name=\"qmui_selected_border_width\"/>\n        <attr name=\"qmui_corner_radius\"/>\n        <attr name=\"qmui_is_circle\"/>\n        <attr name=\"qmui_is_touch_select_mode_enabled\"/>\n    </declare-styleable>\n\n    <attr name=\"QMUIRadiusImageViewStyle\" format=\"reference\"/>\n\n    <!--************** QMUIQQFaceView ****************-->\n    <declare-styleable name=\"QMUIQQFaceView\">\n        <attr name=\"android:textColor\"/>\n        <attr name=\"android:textSize\"/>\n        <attr name=\"android:singleLine\"/>\n        <attr name=\"android:maxLines\"/>\n        <attr name=\"android:lineSpacingExtra\"/>\n        <attr name=\"android:ellipsize\"/>\n        <attr name=\"android:maxWidth\"/>\n        <attr name=\"android:text\"/>\n        <attr name=\"qmui_special_drawable_padding\" format=\"dimension\"/>\n        <attr name=\"qmui_more_action_text\" format=\"string\"/>\n        <attr name=\"qmui_more_action_color\" format=\"color\"/>\n        <attr name=\"qmui_more_action_bg_color\" format=\"color\"/>\n    </declare-styleable>\n    <attr name=\"QMUIQQFaceStyle\" format=\"reference\"/>\n\n    <!-- QMUIFloatLayout start -->\n    <attr name=\"qmui_childHorizontalSpacing\" format=\"dimension\"/>\n    <attr name=\"qmui_childVerticalSpacing\" format=\"dimension\"/>\n    <attr name=\"qmui_maxNumber\" format=\"integer\"/>\n    <declare-styleable name=\"QMUIFloatLayout\">\n        <attr name=\"android:gravity\"/>\n        <attr name=\"qmui_childHorizontalSpacing\"/>\n        <attr name=\"qmui_childVerticalSpacing\"/>\n        <attr name=\"android:maxLines\"/>\n        <attr name=\"qmui_maxNumber\"/>\n    </declare-styleable>\n    <!-- QMUIFloatLayout end -->\n\n    <!-- EmptyView start -->\n    <declare-styleable name=\"QMUIEmptyView\">\n        <attr name=\"qmui_show_loading\" format=\"boolean\"/>\n        <attr name=\"qmui_title_text\" format=\"string\"/>\n        <attr name=\"qmui_detail_text\" format=\"string\"/>\n        <attr name=\"qmui_btn_text\" format=\"string\"/>\n    </declare-styleable>\n    <!-- EmptyView end -->\n\n    <!-- QMUIProgressBar start -->\n    <declare-styleable name=\"QMUIProgressBar\">\n        <attr name=\"qmui_type\" format=\"enum\">\n            <enum name=\"type_rect\" value=\"0\"/>\n            <enum name=\"type_round_rect\" value=\"1\"/>\n            <enum name=\"type_circle\" value=\"2\"/>\n            <enum name=\"type_fill_circle\" value=\"3\"/>\n        </attr>\n        <attr name=\"qmui_progress_color\" format=\"color\"/>\n        <attr name=\"qmui_background_color\" format=\"color\"/>\n        <attr name=\"qmui_max_value\" format=\"integer\"/>\n        <attr name=\"qmui_value\" format=\"integer\"/>\n        <attr name=\"qmui_stroke_width\" format=\"dimension\"/>\n        <attr name=\"qmui_stroke_round_cap\" format=\"boolean\"/>\n        <attr name=\"android:textSize\"/>\n        <attr name=\"android:textColor\"/>\n    </declare-styleable>\n    <!-- QMUIProgressBar end -->\n\n    <!-- FontFitTextView start -->\n    <attr name=\"qmui_minTextSize\" format=\"dimension\"/>\n    <attr name=\"qmui_maxTextSize\" format=\"dimension\"/>\n    <declare-styleable name=\"QMUIFontFitTextView\">\n        <attr name=\"qmui_minTextSize\"/>\n        <attr name=\"qmui_maxTextSize\"/>\n    </declare-styleable>\n    <!-- FontFitTextView end -->\n\n    <declare-styleable name=\"QMUILinkTextView\">\n        <attr name=\"qmui_linkBackgroundColor\" format=\"color\"/>\n        <attr name=\"qmui_linkTextColor\" format=\"color\"/>\n    </declare-styleable>\n\n    <!-- QMUICollapsingTopBarLayout start -->\n    <declare-styleable name=\"QMUICollapsingTopBarLayout\">\n        <attr name=\"qmui_expandedTitleMargin\" format=\"dimension\"/>\n        <attr name=\"qmui_expandedTitleMarginStart\" format=\"dimension\"/>\n        <attr name=\"qmui_expandedTitleMarginTop\" format=\"dimension\"/>\n        <attr name=\"qmui_expandedTitleMarginEnd\" format=\"dimension\"/>\n        <attr name=\"qmui_expandedTitleMarginBottom\" format=\"dimension\"/>\n        <attr name=\"qmui_expandedTitleTextAppearance\" format=\"reference\"/>\n        <attr name=\"qmui_collapsedTitleTextAppearance\" format=\"reference\"/>\n        <attr name=\"qmui_contentScrim\" format=\"color\"/>\n        <attr name=\"qmui_followTopBarCommonSkin\" format=\"boolean\"/>\n        <attr name=\"qmui_statusBarScrim\" format=\"color\"/>\n        <attr name=\"qmui_topBarId\" format=\"reference\"/>\n        <attr name=\"qmui_scrimVisibleHeightTrigger\" format=\"dimension\"/>\n        <attr name=\"qmui_scrimAnimationDuration\" format=\"integer\"/>\n        <attr name=\"qmui_collapsedTitleGravity\">\n            <flag name=\"top\" value=\"0x30\"/>\n            <flag name=\"bottom\" value=\"0x50\"/>\n            <flag name=\"left\" value=\"0x03\"/>\n            <flag name=\"right\" value=\"0x05\"/>\n            <flag name=\"center_vertical\" value=\"0x10\"/>\n            <flag name=\"fill_vertical\" value=\"0x70\"/>\n            <flag name=\"center_horizontal\" value=\"0x01\"/>\n            <flag name=\"center\" value=\"0x11\"/>\n            <flag name=\"start\" value=\"0x00800003\"/>\n            <flag name=\"end\" value=\"0x00800005\"/>\n        </attr>\n        <attr name=\"qmui_expandedTitleGravity\">\n            <flag name=\"top\" value=\"0x30\"/>\n            <flag name=\"bottom\" value=\"0x50\"/>\n            <flag name=\"left\" value=\"0x03\"/>\n            <flag name=\"right\" value=\"0x05\"/>\n            <flag name=\"center_vertical\" value=\"0x10\"/>\n            <flag name=\"fill_vertical\" value=\"0x70\"/>\n            <flag name=\"center_horizontal\" value=\"0x01\"/>\n            <flag name=\"center\" value=\"0x11\"/>\n            <flag name=\"start\" value=\"0x00800003\"/>\n            <flag name=\"end\" value=\"0x00800005\"/>\n        </attr>\n        <attr name=\"qmui_titleEnabled\" format=\"boolean\"/>\n        <attr name=\"qmui_title\"/>\n    </declare-styleable>\n    <declare-styleable name=\"QMUICollapsingTopBarLayout_Layout\">\n        <attr name=\"qmui_layout_collapseMode\">\n            <enum name=\"none\" value=\"0\"/>\n            <enum name=\"pin\" value=\"1\"/>\n            <enum name=\"parallax\" value=\"2\"/>\n        </attr>\n        <attr name=\"qmui_layout_collapseParallaxMultiplier\" format=\"float\"/>\n    </declare-styleable>\n    <!-- QMUICollapsingTopBarLayout end -->\n\n    <!-- QMUISlider start -->\n    <declare-styleable name=\"QMUISlider\">\n        <attr name=\"qmui_slider_bar_height\" format=\"dimension\"/>\n        <attr name=\"qmui_slider_bar_normal_color\" format=\"color\"/>\n        <attr name=\"qmui_slider_bar_progress_color\" format=\"color\"/>\n        <attr name=\"qmui_slider_bar_record_progress_color\" format=\"color\"/>\n        <attr name=\"qmui_slider_bar_tick_count\" format=\"integer\"/>\n        <attr name=\"qmui_slider_bar_thumb_size\" format=\"dimension\"/>\n        <attr name=\"qmui_slider_bar_thumb_style_attr\" format=\"string\"/>\n        <attr name=\"qmui_slider_bar_use_clip_children_by_developer\" format=\"boolean\"/>\n        <attr name=\"qmui_slider_bar_padding_hor_for_thumb_shadow\" format=\"dimension\"/>\n        <attr name=\"qmui_slider_bar_padding_ver_for_thumb_shadow\" format=\"dimension\"/>\n        <attr name=\"qmui_slider_bar_constraint_thumb_in_moving\" format=\"boolean\"/>\n    </declare-styleable>\n    <declare-styleable name=\"QMUISeekBar\">\n        <attr name=\"qmui_seek_bar_tick_height\" format=\"dimension\"/>\n        <attr name=\"qmui_seek_bar_tick_width\" format=\"dimension\"/>\n    </declare-styleable>\n    <attr name=\"QMUISliderStyle\" format=\"reference\"/>\n    <attr name=\"QMUISeekBarStyle\" format=\"reference\"/>\n    <attr name=\"QMUISliderThumbStyle\" format=\"reference\"/>\n    <!-- QMUISlider end -->\n\n    <!-- QMUIPullLayout start -->\n    <declare-styleable name=\"QMUIPullLayout\">\n        <attr name=\"qmui_pull_enable_edge\">\n            <flag name=\"left\" value=\"0x1\"/>\n            <flag name=\"top\" value=\"0x2\"/>\n            <flag name=\"right\" value=\"0x4\"/>\n            <flag name=\"bottom\" value=\"0x8\"/>\n        </attr>\n    </declare-styleable>\n    <attr name=\"QMUIPullLayoutStyle\" format=\"reference\"/>\n\n    <declare-styleable name=\"QMUIPullLayout_Layout\">\n        <attr name=\"qmui_is_target\" format=\"boolean\"/>\n        <attr name=\"qmui_pull_edge\" format=\"enum\">\n            <enum name=\"left\" value=\"0x1\"/>\n            <enum name=\"top\" value=\"0x2\"/>\n            <enum name=\"right\" value=\"0x4\"/>\n            <enum name=\"bottom\" value=\"0x8\"/>\n        </attr>\n        <attr name=\"qmui_action_view_init_offset\" format=\"dimension\"/>\n        <attr name=\"qmui_target_view_trigger_offset\" format=\"dimension|enum\">\n            <enum name=\"wrap\" value=\"-2\" />\n        </attr>\n        <attr name=\"qmui_can_over_pull\" format=\"boolean\"/>\n        <attr name=\"qmui_pull_rate\" format=\"float\"/>\n        <attr name=\"qmui_received_fling_fraction\" format=\"float\"/>\n        <attr name=\"qmui_need_receive_fling_from_target_view\" format=\"boolean\"/>\n        <attr name=\"qmui_scroll_speed_per_pixel\" format=\"integer\"/>\n        <attr name=\"qmui_trigger_until_scroll_to_trigger_offset\" format=\"boolean\"/>\n        <attr name=\"qmui_scroll_to_trigger_offset_after_touch_up\" format=\"boolean\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIPullLoadMoreView\">\n        <attr name=\"qmui_pull_load_more_loading_size\" format=\"dimension\"/>\n        <attr name=\"qmui_pull_load_more_text_size\" format=\"dimension\"/>\n        <attr name=\"qmui_pull_load_more_arrow_text_gap\" format=\"dimension\"/>\n        <attr name=\"qmui_pull_load_more_height\" format=\"dimension\"/>\n        <attr name=\"qmui_pull_load_more_arrow\" format=\"reference\"/>\n        <attr name=\"qmui_pull_load_more_pull_text\" format=\"string\"/>\n        <attr name=\"qmui_pull_load_more_release_text\" format=\"string\"/>\n        <attr name=\"qmui_skin_support_pull_load_more_bg_color\"/>\n        <attr name=\"qmui_skin_support_pull_load_more_loading_tint_color\"/>\n        <attr name=\"qmui_skin_support_pull_load_more_arrow_tint_color\"/>\n        <attr name=\"qmui_skin_support_pull_load_more_text_color\"/>\n    </declare-styleable>\n    <attr name=\"QMUIPullLoadMoreStyle\" format=\"reference\"/>\n\n    <!-- QMUIPullLayout end -->\n\n    <declare-styleable name=\"QMUIPriorityLinearLayout_Layout\">\n        <attr name=\"qmui_layout_priority\" format=\"enum\">\n            <enum name=\"disposable\" value=\"1\"/>\n            <enum name=\"mini_content_protection\" value=\"2\"/>\n            <enum name=\"incompressible\" value=\"3\"/>\n        </attr>\n        <attr name=\"qmui_layout_miniContentProtectionSize\" format=\"dimension\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUITextAppearance\">\n        <attr name=\"android:textSize\"/>\n        <attr name=\"android:textColor\"/>\n        <attr name=\"android:textColorHint\"/>\n        <attr name=\"android:textStyle\"/>\n        <attr name=\"android:typeface\"/>\n        <attr name=\"textAllCaps\"/>\n        <attr name=\"android:shadowColor\"/>\n        <attr name=\"android:shadowDy\"/>\n        <attr name=\"android:shadowDx\"/>\n        <attr name=\"android:shadowRadius\"/>\n    </declare-styleable>\n</resources>\n"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs_alpha.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"QMUIAlphaButton\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaConstraintLayout\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaFrameLayout\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaImageButton\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaLinearLayout\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaRelativeLayout\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIAlphaTextView\">\n        <attr name=\"qmui_alpha_pressed\" />\n        <attr name=\"qmui_alpha_disabled\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs_base.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <attr name=\"qmui_content_padding_horizontal\" format=\"dimension\" /> <!-- 已废弃 -->\n    <attr name=\"qmui_content_spacing_horizontal\" format=\"dimension\" /> <!-- margin 和 padding 等使用的内容通用水平间距 -->\n\n    <!--**********************************************\n    *                qmui common color               *\n    **********************************************-->\n    <attr name=\"qmui_config_color_blue\" format=\"color\" />\n    <attr name=\"qmui_config_color_red\" format=\"color\" />\n    <attr name=\"qmui_config_color_black\" format=\"color\" />\n    <attr name=\"qmui_config_color_link\" format=\"color\" />\n    <attr name=\"qmui_config_color_pressed\" format=\"color\" />\n\n    <attr name=\"qmui_config_color_gray_1\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_2\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_3\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_4\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_5\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_6\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_7\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_8\" format=\"color\" />\n    <attr name=\"qmui_config_color_gray_9\" format=\"color\" />\n\n    <attr name=\"qmui_alpha_pressed\" format=\"float\" />\n    <attr name=\"qmui_alpha_disabled\" format=\"float\" />\n\n    <attr name=\"qmui_general_shadow_elevation\" format=\"dimension\" />\n    <attr name=\"qmui_general_shadow_alpha\" format=\"float\" />\n\n\n    <attr name=\"qmui_skin_support_activity_background\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_color_separator\" format=\"color\" />\n    <attr name=\"qmui_skin_support_color_separator_darken\" format=\"color\" />\n    <attr name=\"qmui_skin_support_color_background\" format=\"color\" />\n    <attr name=\"qmui_skin_support_color_background_pressed\" format=\"color\" />\n    <attr name=\"qmui_skin_support_pull_refresh_view_color\" format=\"color\" />\n\n    <!--**********************************************\n    *              qmui common list                  *\n    **********************************************-->\n\n    <attr name=\"qmui_common_list_item_icon_margin_right\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_accessory_margin_left\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_title_v_text_size\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_title_h_text_size\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_title_line_space\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_detail_v_text_size\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_detail_h_text_size\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_detail_line_space\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_detail_h_margin_with_title\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_detail_v_margin_with_title\" format=\"dimension\" />\n    <attr name=\"qmui_common_list_item_holder_margin_with_title\" format=\"dimension\"/>\n\n    <attr name=\"qmui_skin_support_common_list_title_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_common_list_detail_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_common_list_icon_tint_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_common_list_red_point_tint_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_common_list_new_drawable\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_common_list_separator_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_s_common_list_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_common_list_chevron_color\" format=\"color|reference\"/>\n\n\n    <!--**********************************************\n    *             qmui list item bg                  *\n    **********************************************-->\n    <attr name=\"qmui_list_item_height\" format=\"dimension\" />\n    <attr name=\"qmui_list_item_height_higher\" format=\"dimension\" />\n    <attr name=\"qmui_skin_support_s_list_item_bg_1\" format=\"reference\" />\n    <attr name=\"qmui_skin_support_s_list_item_bg_2\" format=\"reference\" />\n\n    <!--**********************************************\n    *             qmui drawbale                      *\n    **********************************************-->\n    <attr name=\"qmui_skin_support_icon_mark\" format=\"reference\" />\n    <attr name=\"qmui_skin_support_s_checkbox\" format=\"reference\" />\n\n\n    <!--**********************************************\n    *                  qmui btn                      *\n    **********************************************-->\n    <attr name=\"qmui_round_btn_text_size\" format=\"dimension\" />\n    <attr name=\"qmui_round_btn_border_width\" format=\"dimension\" />\n\n    <attr name=\"qmui_skin_support_round_btn_bg_color\" format=\"color\" />\n    <attr name=\"qmui_skin_support_round_btn_border_color\" format=\"color\" />\n    <attr name=\"qmui_skin_support_round_btn_text_color\" format=\"color\" />\n\n    <!--**********************************************\n    *                  qmui topbar                   *\n    ***********************************************-->\n    <attr name=\"qmui_topbar_height\" format=\"dimension\" />\n\n    <attr name=\"qmui_skin_support_topbar_separator_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_topbar_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_topbar_title_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_topbar_subtitle_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_topbar_text_btn_color_state_list\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_topbar_image_tint_color\" format=\"color\"/>\n\n    <!--**********************************************\n    *               qmui loading view                *\n    ***********************************************-->\n    <attr name=\"qmui_loading_size\" format=\"dimension\" />\n    <attr name=\"qmui_skin_support_loading_color\" format=\"color\" />\n\n\n    <!--**********************************************\n    *               qmui load more view              *\n    ***********************************************-->\n    <attr name=\"qmui_skin_support_pull_load_more_bg_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_pull_load_more_loading_tint_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_pull_load_more_arrow_tint_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_pull_load_more_text_color\" format=\"color\"/>\n\n    <!--**********************************************\n    *                  qmui tabSegment              *\n    ***********************************************-->\n    <attr name=\"qmui_skin_support_tab_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_tab_separator_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_tab_normal_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_tab_selected_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_tab_sign_count_view_text_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_tab_sign_count_view_bg_color\" format=\"color|reference\"/>\n\n\n    <attr name=\"qmui_tab_sign_count_view\" format=\"reference\" />\n    <attr name=\"qmui_tab_sign_count_view_min_size\" format=\"dimension\" />\n    <attr name=\"qmui_tab_sign_count_view_min_size_with_text\" format=\"dimension\" />\n    <attr name=\"qmui_tab_sign_count_view_padding_horizontal\" format=\"dimension\" />\n\n    <!--**********************************************\n    *                  qmui emptyView              *\n    ***********************************************-->\n    <attr name=\"qmui_empty_view_loading_size\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_title_normal_margin_top\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_title_text_size\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_title_margin_hor\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_sub_title_normal_margin_top\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_sub_title_text_size\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_sub_title_margin_hor\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_btn_height\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_btn_margin_hor\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_btn_normal_margin_top\" format=\"dimension\"/>\n    <attr name=\"qmui_empty_view_btn_text_size\" format=\"dimension\"/>\n\n    <attr name=\"qmui_skin_support_empty_view_loading_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_empty_view_title_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_empty_view_sub_title_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_empty_view_btn_bg_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_empty_view_btn_border_color\" format=\"color|reference\"/>\n    <attr name=\"qmui_skin_support_empty_view_btn_text_color\" format=\"color|reference\"/>\n\n    <!--**********************************************\n    *                  qmui slider                   *\n    ***********************************************-->\n    <attr name=\"qmui_skin_support_slider_bar_bg_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_slider_bar_progress_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_slider_record_progress_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_slider_thumb_bg_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_slider_thumb_border_color\" format=\"color\"/>\n    <attr name=\"qmui_skin_support_seek_bar_color\" format=\"color\"/>\n\n    <!--**********************************************\n    *                  qmui popup                    *\n    ***********************************************-->\n    <attr name=\"qmui_popup_radius\" format=\"dimension\" />\n    <attr name=\"qmui_popup_border_width\" format=\"dimension\" />\n    <attr name=\"qmui_popup_shadow_elevation\" format=\"dimension\" />\n    <attr name=\"qmui_popup_shadow_alpha\" format=\"float\" />\n    <attr name=\"qmui_popup_shadow_inset\" format=\"dimension\" />\n    <attr name=\"qmui_popup_arrow_width\" format=\"dimension\"/>\n    <attr name=\"qmui_popup_arrow_height\" format=\"dimension\"/>\n    <attr name=\"qmui_quick_action_item_padding_hor\" format=\"dimension\"/>\n    <attr name=\"qmui_quick_action_item_padding_ver\" format=\"dimension\"/>\n    <attr name=\"qmui_quick_action_item_middle_space\" format=\"dimension\"/>\n    <attr name=\"qmui_quick_action_padding_hor\" format=\"dimension\"/>\n    <attr name=\"qmui_quick_action_more_arrow_width\" format=\"dimension\"/>\n\n    <attr name=\"qmui_skin_support_popup_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_popup_border_color\" format=\"reference|color\" />\n    <attr name=\"qmui_skin_support_popup_close_icon\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_quick_action_item_tint_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_quick_action_more_left_arrow\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_quick_action_more_right_arrow\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_quick_action_more_tint_color\" format=\"reference\"/>\n\n    <!--**********************************************\n    *                qmui dialog                     *\n    **********************************************-->\n    <attr name=\"qmui_skin_support_dialog_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_title_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_message_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_action_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_action_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_positive_action_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_negative_action_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_action_container_separator_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_action_divider_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_s_dialog_check_drawable\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_mark_drawable\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_edit_bottom_line_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_edit_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_edit_text_hint_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_s_dialog_menu_item_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_dialog_menu_item_text_color\" format=\"reference\"/>\n\n\n\n\n\n    <attr name=\"qmui_dialog_radius\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_min_width\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_max_width\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_inset_hor\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_inset_ver\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_background_dim_amount\" format=\"float\" />\n    <attr name=\"qmui_dialog_padding_horizontal\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_edit_margin_top\" format=\"dimension\"/>\n    <attr name=\"qmui_dialog_edit_margin_bottom\" format=\"dimension\"/>\n    <attr name=\"qmui_dialog_edit_bottom_line_height\" format=\"dimension\"/>\n\n    <attr name=\"qmui_dialog_title_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_action_container_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_action_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_message_content_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_edit_content_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_menu_container_style\" format=\"reference\" />\n    <attr name=\"qmui_dialog_menu_item_style\" format=\"reference\" />\n\n    <!--**********************************************\n     *              qmui tip dialog                  *\n     **********************************************-->\n    <attr name=\"qmui_tip_dialog_min_width\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_min_height\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_max_width\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_padding_vertical\" format=\"dimension\" />\n    <attr name=\"qmui_tip_dialog_padding_horizontal\" format=\"dimension\" />\n    <attr name=\"qmui_tip_dialog_radius\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_loading_size\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_text_margin_top\" format=\"dimension\"/>\n    <attr name=\"qmui_tip_dialog_text_size\" format=\"dimension\"/>\n\n    <attr name=\"qmui_skin_support_tip_dialog_bg\" format=\"reference\" />\n    <attr name=\"qmui_skin_support_tip_dialog_loading_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_tip_dialog_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_tip_dialog_icon_success_src\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_tip_dialog_icon_error_src\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_tip_dialog_icon_info_src\" format=\"reference\"/>\n\n    <!--**********************************************\n    *              qmui bottom sheet                 *\n    **********************************************-->\n    <attr name=\"qmui_skin_support_bottom_sheet_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_title_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_cancel_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_cancel_bg\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_separator_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_list_item_bg\" format=\"reference\" />\n    <attr name=\"qmui_skin_support_bottom_sheet_list_item_text_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_list_red_point_color\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_list_mark\" format=\"reference\"/>\n    <attr name=\"qmui_skin_support_bottom_sheet_grid_item_text_color\" format=\"reference\"/>\n\n    <attr name=\"qmui_bottom_sheet_radius\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_padding_hor\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_use_percent_min_height\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_height_percent\" format=\"float\" />\n    <attr name=\"qmui_bottom_sheet_max_width\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_background_dim_amount\" format=\"float\" />\n    <attr name=\"qmui_bottom_sheet_cancel_btn_height\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_list_item_height\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_list_item_separator_height\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_list_item_icon_size\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_list_item_icon_margin_right\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_list_item_red_point_size\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_list_item_mark_margin_left\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_list_item_tip_point_margin_left\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_padding_top\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_padding_bottom\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_line_vertical_space\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_item_text_size\" format=\"dimension\"/>\n    <attr name=\"qmui_bottom_sheet_grid_item_icon_size\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_item_padding_top\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_item_padding_bottom\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_item_text_margin_top\" format=\"dimension\" />\n    <attr name=\"qmui_bottom_sheet_grid_item_mini_width\" format=\"dimension\" />\n\n    <attr name=\"qmui_bottom_sheet_title_style\" format=\"reference\" />\n    <attr name=\"qmui_bottom_sheet_cancel_style\" format=\"reference\" />\n    <attr name=\"qmui_bottom_sheet_list_item_text_style\" format=\"reference\"/>\n    <attr name=\"qmui_bottom_sheet_grid_item_text_style\" format=\"reference\"/>\n\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <attr name=\"qmui_paddingTopWhenNotTitle\" format=\"dimension\" />\n    <attr name=\"qmui_paddingBottomWhenNotContent\" format=\"dimension\" />\n    <attr name=\"qmui_dialog_menu_item_check_mark_margin_hor\" format=\"dimension\" />\n\n    <declare-styleable name=\"QMUITextCommonStyleDef\">\n        <attr name=\"android:textColor\" />\n        <attr name=\"android:textSize\" />\n        <attr name=\"android:background\" />\n        <attr name=\"android:gravity\" />\n        <attr name=\"android:paddingTop\" />\n        <attr name=\"android:paddingLeft\" />\n        <attr name=\"android:paddingRight\" />\n        <attr name=\"android:paddingBottom\" />\n        <attr name=\"android:singleLine\" />\n        <attr name=\"android:ellipsize\" />\n        <attr name=\"android:maxLines\" />\n        <attr name=\"android:lineSpacingExtra\" />\n        <attr name=\"android:drawablePadding\" />\n        <attr name=\"android:textColorHint\" />\n        <attr name=\"android:textStyle\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogTitleTvCustomDef\">\n        <attr name=\"qmui_paddingBottomWhenNotContent\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogMessageTvCustomDef\">\n        <attr name=\"qmui_paddingTopWhenNotTitle\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogActionContainerCustomDef\">\n        <attr name=\"qmui_dialog_action_container_justify_content\" format=\"enum\">\n            <enum name=\"start\" value=\"0\" />\n            <enum name=\"end\" value=\"1\" />\n            <enum name=\"stretch\" value=\"2\" />\n            <enum name=\"custom\" value=\"3\" />\n        </attr>\n        <attr name=\"qmui_dialog_action_container_custom_space_index\" format=\"integer\" />\n        <attr name=\"qmui_dialog_action_height\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_action_space\" format=\"dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogActionStyleDef\">\n        <attr name=\"android:textColor\" />\n        <attr name=\"android:textSize\" />\n        <attr name=\"android:background\" />\n        <attr name=\"android:gravity\" />\n        <attr name=\"android:minWidth\" />\n        <attr name=\"android:textStyle\" />\n        <attr name=\"qmui_dialog_action_button_padding_horizontal\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_action_icon_space\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_positive_action_text_color\" format=\"color|reference\" />\n        <attr name=\"qmui_dialog_negative_action_text_color\" format=\"color|reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogMenuContainerStyleDef\">\n        <attr name=\"android:paddingTop\" />\n        <attr name=\"android:paddingBottom\" />\n        <attr name=\"qmui_dialog_menu_container_single_padding_vertical\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_menu_container_padding_top_when_title_exist\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_menu_container_padding_bottom_when_action_exist\" format=\"dimension\" />\n        <attr name=\"qmui_dialog_menu_item_height\" format=\"dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogMenuTextStyleDef\">\n        <attr name=\"android:textColor\" />\n        <attr name=\"android:textSize\" />\n        <attr name=\"android:gravity\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogMenuMarkDef\">\n        <attr name=\"qmui_dialog_menu_item_mark_drawable\" format=\"reference\" />\n        <attr name=\"qmui_dialog_menu_item_check_mark_margin_hor\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIDialogMenuCheckDef\">\n        <attr name=\"qmui_dialog_menu_item_check_drawable\" format=\"reference\" />\n        <attr name=\"qmui_dialog_menu_item_check_mark_margin_hor\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUISkinDef\">\n        <attr name=\"qmui_skin_background\" format=\"string\" />\n        <attr name=\"qmui_skin_text_color\" format=\"string\" />\n        <attr name=\"qmui_skin_second_text_color\" format=\"string\" />\n        <attr name=\"qmui_skin_src\" format=\"string\" />\n        <attr name=\"qmui_skin_border\" format=\"string\" />\n        <attr name=\"qmui_skin_separator_top\" format=\"string\" />\n        <attr name=\"qmui_skin_separator_right\" format=\"string\" />\n        <attr name=\"qmui_skin_separator_bottom\" format=\"string\" />\n        <attr name=\"qmui_skin_separator_left\" format=\"string\" />\n        <attr name=\"qmui_skin_alpha\" format=\"string\" />\n        <attr name=\"qmui_skin_tint_color\" format=\"string\" />\n        <attr name=\"qmui_skin_bg_tint_color\" format=\"string\" />\n        <attr name=\"qmui_skin_progress_color\" format=\"string\" />\n        <attr name=\"qmui_skin_underline\" format=\"string\"/>\n        <attr name=\"qmui_skin_more_text_color\" format=\"string\"/>\n        <attr name=\"qmui_skin_more_bg_color\" format=\"string\"/>\n        <attr name=\"qmui_skin_hint_color\" format=\"string\"/>\n        <attr name=\"qmui_skin_text_compound_tint_color\" format=\"string\"/>\n        <attr name=\"qmui_skin_text_compound_src_left\" format=\"string\"/>\n        <attr name=\"qmui_skin_text_compound_src_top\" format=\"string\"/>\n        <attr name=\"qmui_skin_text_compound_src_right\" format=\"string\"/>\n        <attr name=\"qmui_skin_text_compound_src_bottom\" format=\"string\"/>\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"QMUILayout\">\n        <attr name=\"android:maxWidth\" />\n        <attr name=\"android:maxHeight\" />\n        <attr name=\"android:minWidth\" />\n        <attr name=\"android:minHeight\" />\n        <attr name=\"qmui_bottomDividerHeight\" format=\"dimension\" />\n        <attr name=\"qmui_bottomDividerColor\" format=\"color|reference\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" format=\"dimension\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" format=\"dimension\" />\n        <attr name=\"qmui_topDividerHeight\" format=\"dimension\" />\n        <attr name=\"qmui_topDividerColor\" format=\"color|reference\" />\n        <attr name=\"qmui_topDividerInsetLeft\" format=\"dimension\" />\n        <attr name=\"qmui_topDividerInsetRight\" format=\"dimension\" />\n        <attr name=\"qmui_leftDividerWidth\" format=\"dimension\" />\n        <attr name=\"qmui_leftDividerColor\" format=\"color|reference\" />\n        <attr name=\"qmui_leftDividerInsetTop\" format=\"dimension\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" format=\"dimension\" />\n        <attr name=\"qmui_rightDividerWidth\" format=\"dimension\" />\n        <attr name=\"qmui_rightDividerColor\" format=\"color|reference\" />\n        <attr name=\"qmui_rightDividerInsetTop\" format=\"dimension\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" format=\"dimension\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" format=\"color|reference\" />\n        <attr name=\"qmui_hideRadiusSide\" format=\"enum\">\n            <enum name=\"none\" value=\"0\" />\n            <enum name=\"top\" value=\"1\" />\n            <enum name=\"right\" value=\"2\" />\n            <enum name=\"bottom\" value=\"3\" />\n            <enum name=\"left\" value=\"4\" />\n        </attr>\n        <attr name=\"qmui_showBorderOnlyBeforeL\" format=\"boolean\" />\n        <attr name=\"qmui_shadowElevation\" format=\"dimension\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" format=\"boolean\" />\n        <attr name=\"qmui_shadowAlpha\" format=\"float\" />\n        <attr name=\"qmui_outlineInsetTop\" format=\"dimension\" />\n        <attr name=\"qmui_outlineInsetLeft\" format=\"dimension\" />\n        <attr name=\"qmui_outlineInsetRight\" format=\"dimension\" />\n        <attr name=\"qmui_outlineInsetBottom\" format=\"dimension\" />\n        <attr name=\"qmui_outlineExcludePadding\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIButton\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIConstraintLayout\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIFrameLayout\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUILinearLayout\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIPriorityLinearLayout\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIRelativeLayout\">\n        <attr name=\"qmui_bottomDividerHeight\" />\n        <attr name=\"qmui_bottomDividerColor\" />\n        <attr name=\"qmui_bottomDividerInsetLeft\" />\n        <attr name=\"qmui_bottomDividerInsetRight\" />\n        <attr name=\"qmui_topDividerHeight\" />\n        <attr name=\"qmui_topDividerColor\" />\n        <attr name=\"qmui_topDividerInsetLeft\" />\n        <attr name=\"qmui_topDividerInsetRight\" />\n        <attr name=\"qmui_leftDividerWidth\" />\n        <attr name=\"qmui_leftDividerColor\" />\n        <attr name=\"qmui_leftDividerInsetTop\" />\n        <attr name=\"qmui_leftDividerInsetBottom\" />\n        <attr name=\"qmui_rightDividerWidth\" />\n        <attr name=\"qmui_rightDividerColor\" />\n        <attr name=\"qmui_rightDividerInsetTop\" />\n        <attr name=\"qmui_rightDividerInsetBottom\" />\n        <attr name=\"qmui_radius\" />\n        <attr name=\"qmui_borderColor\" />\n        <attr name=\"qmui_borderWidth\" />\n        <attr name=\"qmui_outerNormalColor\" />\n        <attr name=\"qmui_hideRadiusSide\" />\n        <attr name=\"qmui_showBorderOnlyBeforeL\" />\n        <attr name=\"qmui_shadowElevation\" />\n        <attr name=\"qmui_useThemeGeneralShadowElevation\" />\n        <attr name=\"qmui_shadowAlpha\" />\n        <attr name=\"qmui_outlineInsetTop\" />\n        <attr name=\"qmui_outlineInsetLeft\" />\n        <attr name=\"qmui_outlineInsetRight\" />\n        <attr name=\"qmui_outlineInsetBottom\" />\n        <attr name=\"qmui_outlineExcludePadding\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_attrs_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- RoundWidget start -->\n\n    <!-- 圆角是否要自适应为 View 高度的一半 -->\n    <attr name=\"qmui_isRadiusAdjustBounds\" format=\"boolean\"/>\n    <!-- 同时指定四个方向的圆角大小 -->\n    <attr name=\"qmui_radius\" format=\"dimension\"/>\n    <!-- 指定左上方圆角的大小 -->\n    <attr name=\"qmui_radiusTopLeft\" format=\"dimension\"/>\n    <!-- 指定右上方圆角的大小 -->\n    <attr name=\"qmui_radiusTopRight\" format=\"dimension\"/>\n    <!-- 指定左下方圆角的大小 -->\n    <attr name=\"qmui_radiusBottomLeft\" format=\"dimension\"/>\n    <!-- 指定右下方圆角的大小 -->\n    <attr name=\"qmui_radiusBottomRight\" format=\"dimension\"/>\n\n    <attr name=\"QMUIButtonStyle\" format=\"reference\"/>\n\n    <declare-styleable name=\"QMUIRoundButton\">\n        <attr name=\"qmui_backgroundColor\"/>\n        <attr name=\"qmui_borderColor\"/>\n        <attr name=\"qmui_borderWidth\"/>\n        <attr name=\"qmui_isRadiusAdjustBounds\"/>\n        <attr name=\"qmui_radius\"/>\n        <attr name=\"qmui_radiusTopLeft\"/>\n        <attr name=\"qmui_radiusTopRight\"/>\n        <attr name=\"qmui_radiusBottomLeft\"/>\n        <attr name=\"qmui_radiusBottomRight\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIRoundFrameLayout\">\n        <attr name=\"qmui_backgroundColor\"/>\n        <attr name=\"qmui_borderColor\"/>\n        <attr name=\"qmui_borderWidth\"/>\n        <attr name=\"qmui_isRadiusAdjustBounds\"/>\n        <attr name=\"qmui_radius\"/>\n        <attr name=\"qmui_radiusTopLeft\"/>\n        <attr name=\"qmui_radiusTopRight\"/>\n        <attr name=\"qmui_radiusBottomLeft\"/>\n        <attr name=\"qmui_radiusBottomRight\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIRoundLinearLayout\">\n        <attr name=\"qmui_backgroundColor\"/>\n        <attr name=\"qmui_borderColor\"/>\n        <attr name=\"qmui_borderWidth\"/>\n        <attr name=\"qmui_isRadiusAdjustBounds\"/>\n        <attr name=\"qmui_radius\"/>\n        <attr name=\"qmui_radiusTopLeft\"/>\n        <attr name=\"qmui_radiusTopRight\"/>\n        <attr name=\"qmui_radiusBottomLeft\"/>\n        <attr name=\"qmui_radiusBottomRight\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"QMUIRoundRelativeLayout\">\n        <attr name=\"qmui_backgroundColor\"/>\n        <attr name=\"qmui_borderColor\"/>\n        <attr name=\"qmui_borderWidth\"/>\n        <attr name=\"qmui_isRadiusAdjustBounds\"/>\n        <attr name=\"qmui_radius\"/>\n        <attr name=\"qmui_radiusTopLeft\"/>\n        <attr name=\"qmui_radiusTopRight\"/>\n        <attr name=\"qmui_radiusBottomLeft\"/>\n        <attr name=\"qmui_radiusBottomRight\"/>\n    </declare-styleable>\n\n    <!-- RoundWidget end -->\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!--*********************************************\n    *              Config Color                  *\n    **********************************************-->\n\n    <color name=\"qmui_config_color_transparent\">#00000000</color>\n    <color name=\"qmui_config_color_white\">#ffffff</color>\n    <color name=\"qmui_config_color_75_white\">#C0FFFFFF</color>\n    <color name=\"qmui_config_color_50_white\">#80FFFFFF</color>\n    <color name=\"qmui_config_color_25_white\">#40FFFFFF</color>\n    <color name=\"qmui_config_color_15_white\">#26FFFFFF</color>\n    <color name=\"qmui_config_color_10_white\">#19FFFFFF</color>\n\n    <color name=\"qmui_config_color_pure_black\">#000000</color>\n    <color name=\"qmui_config_color_75_pure_black\">#C0000000</color>\n    <color name=\"qmui_config_color_60_pure_black\">#99000000</color>\n    <color name=\"qmui_config_color_50_pure_black\">#80000000</color>\n    <color name=\"qmui_config_color_25_pure_black\">#40000000</color>\n    <color name=\"qmui_config_color_15_pure_black\">#26000000</color>\n    <color name=\"qmui_config_color_10_pure_black\">#19000000</color>\n\n\n\n    <color name=\"qmui_config_color_blue\">#1B88EE</color>\n    <color name=\"qmui_config_color_50_blue\">#801B88EE</color>\n    <color name=\"qmui_config_color_red\">#FA3A3A</color>\n    <color name=\"qmui_config_color_separator\">#DEE0E2</color>\n    <color name=\"qmui_config_color_separator_darken\">#D4D6D8</color>\n    <color name=\"qmui_config_color_background\">#F4F5F7</color>\n    <color name=\"qmui_config_color_background_pressed\">#EEEFF1</color>\n    <color name=\"qmui_config_color_black\">#212832</color>\n    <color name=\"qmui_config_color_link\">#547FB0</color>\n    <color name=\"qmui_config_color_pressed\">#EEEEF0</color>\n\n    <color name=\"qmui_config_color_gray_1\">#353C46</color>\n    <color name=\"qmui_config_color_gray_2\">#49505A</color>\n    <color name=\"qmui_config_color_gray_3\">#5D646E</color>\n    <color name=\"qmui_config_color_gray_4\">#717882</color>\n    <color name=\"qmui_config_color_gray_5\">#858C96</color>\n    <color name=\"qmui_config_color_gray_6\">#99A0AA</color>\n    <color name=\"qmui_config_color_gray_7\">#ADB4BE</color>\n    <color name=\"qmui_config_color_gray_8\">#C4C8D0</color>\n    <color name=\"qmui_config_color_gray_9\">#D8DCE4</color>\n\n\n    <color name=\"qmui_drawable_color_list_separator\">#DEE0E2</color>\n    <color name=\"qmui_drawable_color_list_pressed\">#DEE0E2</color>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <!--**********************************************\n    *                  Dialog                    *\n    **********************************************-->\n    <dimen name=\"qmui_dialog_radius\">2dp</dimen>\n\n    <!--**********************************************\n    *                  Button                        *\n    **********************************************-->\n    <dimen name=\"qmui_btn_text_size\">16sp</dimen>\n    <dimen name=\"qmui_btn_border_width\">1dp</dimen>\n\n    <!--**********************************************\n    *                  Switch                        *\n    **********************************************-->\n    <dimen name=\"qmui_switch_size\">20dp</dimen>\n\n    <!--**********************************************\n    *           List - List Item                 *\n    **********************************************-->\n\n    <dimen name=\"qmui_list_item_inset_left\">16dp</dimen>\n    <dimen name=\"qmui_list_divider_height\">1px</dimen>\n    <dimen name=\"qmui_list_divider_height_negative\">-1px</dimen>\n    <dimen name=\"qmui_group_list_section_header_footer_text_size\">13sp</dimen>\n\n    <!--**********************************************\n    *                TabSegment                  *\n    **********************************************-->\n    <dimen name=\"qmui_tab_segment_indicator_height\">2dp</dimen>\n    <dimen name=\"qmui_tab_segment_text_size\">16sp</dimen>\n    <dimen name=\"qmui_tab_sign_count_view_min_size_with_text\">14dp</dimen>\n    <dimen name=\"qmui_tab_sign_count_view_min_size\">6dp</dimen>\n\n\n    <!--**********************************************\n    *                   GroupList                    *\n    **********************************************-->\n    <dimen name=\"qmui_list_item_height\">56dp</dimen>\n    <dimen name=\"qmui_list_item_height_higher\">103dp</dimen>\n    <dimen name=\"qmui_group_list_section_header_footer_padding_vertical\">8dp</dimen>\n    <dimen name=\"qmui_group_list_section_header_footer_textSize\">13sp</dimen>\n\n    <dimen name=\"qmui_rv_swipe_action_escape_max_velocity\">800dp</dimen>\n    <dimen name=\"qmui_rv_swipe_action_escape_velocity\">120dp</dimen>\n\n\n    <!-- 全局统一的界面左右间距，例如列表分隔线inset -->\n    <dimen name=\"qmui_content_padding_horizontal\">@dimen/qmui_content_spacing_horizontal\n    </dimen> <!-- 已废弃 -->\n    <dimen name=\"qmui_content_spacing_horizontal\">16dp\n    </dimen> <!-- margin 和 padding 等使用的内容通用水平间距 -->\n    <!-- 提示标点 -->\n    <dimen name=\"qmui_tips_point_size\">8dp</dimen>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!-- dialog -->\n    <item name=\"qmui_dialog_root_layout\" type=\"id\"/>\n    <item name=\"qmui_dialog_layout\" type=\"id\"/>\n    <item name=\"qmui_dialog_title_id\" type=\"id\"/>\n    <item name=\"qmui_dialog_operator_layout_id\" type=\"id\"/>\n    <item name=\"qmui_dialog_content_id\" type=\"id\"/>\n    <item name=\"qmui_dialog_edit_input\" type=\"id\"/>\n    <item name=\"qmui_dialog_edit_right_icon\" type=\"id\"/>\n    <item name=\"qmui_bottom_sheet_title\" type=\"id\"/>\n    <item name=\"qmui_bottom_sheet_cancel\" type=\"id\"/>\n\n    <item name=\"qmui_tip_content_id\" type=\"id\" />\n    \n    <item name=\"qmui_tab_segment_item_id\" type=\"id\"/>\n\n    <item name=\"qmui_view_can_not_cache_tag\" type=\"id\"/>\n\n    <item name=\"qmui_topbar_item_left_back\" type=\"id\"/>\n\n    <item name=\"qmui_view_offset_helper\" type=\"id\"/>\n\n    <item name=\"qmui_popup_close_btn_id\" type=\"id\"/>\n\n    <item name=\"qmui_skin_current\" type=\"id\"/>\n    <item name=\"qmui_skin_value\" type=\"id\" />\n    <item name=\"qmui_skin_default_attr_provider\" type=\"id\" />\n    <item name=\"qmui_skin_apply_listener\" type=\"id\" />\n    <item name=\"qmui_skin_skip_for_maker\" type=\"id\"/>\n    <item name=\"qmui_skin_adapter\" type=\"id\"/>\n    <item name=\"qmui_skin_intercept_dispatch\" type=\"id\"/>\n    <item name=\"qmui_skin_ignore_apply\" type=\"id\"/>\n\n    <item name=\"qmui_click_timestamp\" type=\"id\"/>\n    <item name=\"qmui_click_debounce_action\" type=\"id\"/>\n\n    <item name=\"qmui_exposure_register\" type=\"id\"/>\n    <item name=\"qmui_exposure_config\" type=\"id\"/>\n    <item name=\"qmui_exposure_data\" type=\"id\"/>\n    <item name=\"qmui_exposure_effect_list\" type=\"id\"/>\n    <item name=\"qmui_exposure_last_data\" type=\"id\"/>\n    <item name=\"qmui_exposure_ing\" type=\"id\"/>\n    <item name=\"qmui_exposure_holder\" type=\"id\"/>\n    <item name=\"qmui_exposure_debounce\" type=\"id\"/>\n    <item name=\"qmui_exposure_parent_expose_request\" type=\"id\"/>\n    <item name=\"qmui_exposure_recycler_collection\" type=\"id\"/>\n    <item name=\"qmui_exposure_custom_effect\" type=\"id\"/>\n    <item name=\"qmui_exposure_is_recycler_container\" type=\"id\"/>\n    <item name=\"qmui_exposure_custom_check_trigger\" type=\"id\" />\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!-- common 公共 -->\n    <!-- 零宽空格，用于修复小米等系统对字符的缩略错误 -->\n    <string name=\"qmui_tool_fixellipsize\">\\u200b</string>\n\n    <string name=\"app_name\">QMUI</string>\n    <string name=\"qmui_cancel\">取&#160;消</string>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_style_appearance.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <style name=\"QMUITextAppearance\" parent=\"TextAppearanceBase\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_black</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:textColorHighlight\">?attr/qmui_skin_support_color_background_pressed</item>\n    </style>\n\n    <style name=\"QMUITextAppearance.Title\"/>\n    <style name=\"QMUITextAppearance.Title.Gray\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_3</item>\n    </style>\n    <style name=\"QMUITextAppearance.Title.Large\">\n        <item name=\"android:textSize\">16sp</item>\n    </style>\n    <style name=\"QMUITextAppearance.ListItem\">\n        <item name=\"android:textColor\">@color/qmui_s_list_item_text_color</item>\n    </style>\n\n    <style name=\"QMUITextAppearance.GridItem\"/>\n    <style name=\"QMUITextAppearance.GridItem.Small\">\n        <item name=\"android:textColor\">@color/qmui_config_color_gray_3</item>\n        <item name=\"android:textSize\">11sp</item>\n    </style>\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_style_widget.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!--********************* TabSegment *********************-->\n    <style name=\"QMUI.TabSegment\">\n        <item name=\"qmui_tab_has_indicator\">false</item>\n        <item name=\"qmui_tab_indicator_height\">@dimen/qmui_tab_segment_indicator_height</item>\n        <item name=\"qmui_tab_normal_text_size\">@dimen/qmui_tab_segment_text_size</item>\n        <item name=\"qmui_tab_selected_text_size\">@dimen/qmui_tab_segment_text_size</item>\n        <item name=\"qmui_tab_indicator_top\">false</item>\n        <item name=\"qmui_tab_icon_position\">left</item>\n    </style>\n\n    <style name=\"QMUI.TabSegment.SignCount\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">?attr/qmui_tab_sign_count_view_min_size</item>\n        <item name=\"qmui_backgroundColor\">?attr/qmui_skin_support_tab_sign_count_view_bg_color\n        </item>\n        <item name=\"qmui_isRadiusAdjustBounds\">true</item>\n        <item name=\"android:textSize\">10sp</item>\n        <item name=\"android:textColor\">?attr/qmui_skin_support_tab_sign_count_view_text_color</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:singleLine\">true</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_tab_sign_count_view_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_tab_sign_count_view_padding_horizontal</item>\n        <item name=\"android:minWidth\">?attr/qmui_tab_sign_count_view_min_size</item>\n        <item name=\"android:minHeight\">?attr/qmui_tab_sign_count_view_min_size</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingBottom\">0dp</item>\n        <item name=\"android:ellipsize\">end</item>\n    </style>\n\n    <!--********************* Button *********************-->\n    <style name=\"QMUI.RoundButton\" parent=\"@style/Button.Compat\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <!--<item name=\"android:padding\">0dp</item>--><!-- 不用 android:padding，而用各个方向分别指定 padding，方便子类覆盖 -->\n        <item name=\"android:paddingLeft\">0dp</item>\n        <item name=\"android:paddingRight\">0dp</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingBottom\">0dp</item>\n        <item name=\"android:minWidth\">0dp</item>\n        <item name=\"android:minHeight\">0dp</item>\n        <item name=\"android:singleLine\">true</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"qmui_isRadiusAdjustBounds\">true</item>\n        <item name=\"qmui_backgroundColor\">?attr/qmui_skin_support_round_btn_bg_color</item>\n        <item name=\"qmui_borderWidth\">?attr/qmui_round_btn_border_width</item>\n        <item name=\"qmui_borderColor\">?attr/qmui_skin_support_round_btn_border_color</item>\n        <item name=\"android:textColor\">?attr/qmui_skin_support_round_btn_text_color</item>\n    </style>\n\n\n    <style name=\"QMUI.CommonListItemView\">\n        <item name=\"android:paddingLeft\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"android:background\">?attr/qmui_skin_support_s_list_item_bg_2</item>\n        <item name=\"qmui_common_list_title_color\">?attr/qmui_skin_support_common_list_title_color\n        </item>\n        <item name=\"qmui_common_list_detail_color\">\n            ?attr/qmui_skin_support_common_list_detail_color\n        </item>\n    </style>\n\n    <style name=\"QMUI.GroupListSectionView\">\n        <item name=\"android:paddingLeft\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"android:paddingTop\">\n            @dimen/qmui_group_list_section_header_footer_padding_vertical\n        </item>\n        <item name=\"android:paddingBottom\">\n            @dimen/qmui_group_list_section_header_footer_padding_vertical\n        </item>\n    </style>\n\n    <!--********************* Loading *********************-->\n    <style name=\"QMUI.Loading\">\n        <item name=\"qmui_loading_view_size\">?attr/qmui_loading_size</item>\n        <item name=\"android:color\">?attr/qmui_skin_support_loading_color</item>\n    </style>\n\n    <style name=\"QMUI.Loading.White\">\n        <item name=\"qmui_loading_view_size\">?attr/qmui_loading_size</item>\n        <item name=\"android:color\">@color/qmui_config_color_white</item>\n    </style>\n\n    <!--********************* TopBar *********************-->\n    <style name=\"QMUI.TopBar\">\n        <item name=\"android:background\">?attr/qmui_skin_support_topbar_bg</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"qmui_bottomDividerHeight\">1px</item>\n        <item name=\"qmui_bottomDividerColor\">?attr/qmui_skin_support_topbar_separator_color</item>\n        <item name=\"qmui_topbar_left_back_drawable_id\">@drawable/qmui_icon_topbar_back</item>\n        <item name=\"qmui_topbar_clear_left_padding_when_add_left_back_view\">false</item>\n        <item name=\"qmui_topbar_left_back_width\">-1dp</item>\n        <item name=\"qmui_topbar_title_gravity\">center</item>\n        <item name=\"android:paddingLeft\">4dp</item>\n        <item name=\"android:paddingRight\">4dp</item>\n        <item name=\"qmui_topbar_title_color\">?attr/qmui_skin_support_topbar_title_color</item>\n        <item name=\"qmui_topbar_title_text_size\">17sp</item>\n        <item name=\"qmui_topbar_title_bold\">false</item>\n        <item name=\"qmui_topbar_title_text_size_with_subtitle\">16sp</item>\n        <item name=\"qmui_topbar_title_margin_horizontal_when_no_btn_aside\">0dp</item>\n        <item name=\"qmui_topbar_title_container_padding_horizontal\">8dp</item>\n        <item name=\"qmui_topbar_subtitle_text_size\">11sp</item>\n        <item name=\"qmui_topbar_subtitle_color\">?attr/qmui_skin_support_topbar_subtitle_color</item>\n        <item name=\"qmui_topbar_subtitle_bold\">false</item>\n        <item name=\"qmui_topbar_image_btn_width\">48dp</item>\n        <item name=\"qmui_topbar_image_btn_height\">48dp</item>\n        <item name=\"qmui_topbar_text_btn_padding_horizontal\">12dp</item>\n        <item name=\"qmui_topbar_text_btn_color_state_list\">\n            ?attr/qmui_skin_support_topbar_text_btn_color_state_list\n        </item>\n        <item name=\"qmui_topbar_text_btn_text_size\">16sp</item>\n        <item name=\"qmui_topbar_text_btn_bold\">false</item>\n    </style>\n\n    <style name=\"QMUI.CollapsingTopBarLayoutExpanded\">\n        <item name=\"android:textColor\">@color/qmui_config_color_white</item>\n        <item name=\"android:textSize\">24sp</item>\n    </style>\n\n    <style name=\"QMUI.CollapsingTopBarLayoutCollapsed\">\n        <item name=\"android:textColor\">@color/qmui_config_color_white</item>\n        <item name=\"android:textSize\">17sp</item>\n    </style>\n\n    <!--********************* Dialog *********************-->\n\n    <style name=\"QMUI.BaseDialog\" parent=\"Theme.AppCompat.Dialog\">\n        <item name=\"android:buttonStyle\">@style/Button.Compat</item>\n    </style>\n\n\n    <style name=\"QMUI.Dialog\" parent=\"QMUI.BaseDialog\">\n        <item name=\"android:backgroundDimAmount\">?attr/qmui_dialog_background_dim_amount</item>\n        <item name=\"android:windowBackground\">@color/qmui_config_color_transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowIsFloating\">true</item><!-- 在中间弹框、背后有遮罩的效果 -->\n    </style>\n\n    <style name=\"QMUI.TipDialog\" parent=\"QMUI.BaseDialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n    </style>\n\n    <style name=\"QMUI.BottomSheet\" parent=\"QMUI.BaseDialog\">\n        <item name=\"android:backgroundDimAmount\">?attr/qmui_bottom_sheet_background_dim_amount\n        </item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.Title\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_dialog_title_text_color</item>\n        <item name=\"android:textSize\">17sp</item>\n        <item name=\"android:gravity\">left</item>\n        <item name=\"android:paddingTop\">24dp</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingBottom\">0dp</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:lineSpacingExtra\">2dp</item>\n        <item name=\"qmui_paddingBottomWhenNotContent\">27dp</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.ActionContainer\">\n        <item name=\"android:paddingLeft\">12dp</item>\n        <item name=\"android:paddingRight\">12dp</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingBottom\">12dp</item>\n        <item name=\"qmui_dialog_action_container_justify_content\">end</item>\n        <item name=\"qmui_dialog_action_height\">36dp</item>\n        <item name=\"qmui_dialog_action_space\">8dp</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.Action\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_blue</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:gravity\">right|center_vertical</item>\n        <item name=\"android:minWidth\">64dp</item>\n        <item name=\"android:background\">?attr/qmui_skin_support_dialog_action_bg</item>\n        <item name=\"qmui_dialog_action_button_padding_horizontal\">12dp</item>\n        <item name=\"qmui_dialog_action_icon_space\">6dp</item>\n        <item name=\"qmui_dialog_positive_action_text_color\">\n            ?attr/qmui_skin_support_dialog_positive_action_text_color\n        </item>\n        <item name=\"qmui_dialog_negative_action_text_color\">\n            ?attr/qmui_skin_support_dialog_negative_action_text_color\n        </item>\n    </style>\n\n    <style name=\"QMUI.Dialog.MessageContent\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_dialog_message_text_color</item>\n        <item name=\"android:textSize\">16sp</item>\n        <item name=\"android:gravity\">left</item>\n        <item name=\"android:paddingTop\">14dp</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingBottom\">28dp</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:lineSpacingExtra\">3dp</item>\n        <item name=\"android:drawablePadding\">8dp</item>\n        <item name=\"qmui_paddingTopWhenNotTitle\">27dp</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.EditContent\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_dialog_edit_text_color</item>\n        <item name=\"android:textColorHint\">?attr/qmui_skin_support_dialog_edit_text_hint_color\n        </item>\n        <item name=\"android:gravity\">left|bottom</item>\n        <item name=\"android:lineSpacingExtra\">3dp</item>\n        <item name=\"android:drawablePadding\">8dp</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingLeft\">0dp</item>\n        <item name=\"android:paddingRight\">0dp</item>\n        <item name=\"android:paddingBottom\">5dp</item>\n        <item name=\"qmui_paddingTopWhenNotTitle\">7dp</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.MenuContainer\">\n        <item name=\"android:paddingTop\">8dp</item>\n        <item name=\"android:paddingBottom\">8dp</item>\n        <item name=\"qmui_dialog_menu_container_single_padding_vertical\">0dp</item>\n        <item name=\"qmui_dialog_menu_container_padding_top_when_title_exist\">14dp</item>\n        <item name=\"qmui_dialog_menu_container_padding_bottom_when_action_exist\">27dp</item>\n        <item name=\"qmui_dialog_menu_item_height\">48dp</item>\n    </style>\n\n    <style name=\"QMUI.Dialog.MenuItem\">\n        <item name=\"android:background\">?attr/qmui_skin_support_s_dialog_menu_item_bg</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_dialog_padding_horizontal</item>\n        <item name=\"android:paddingBottom\">0dp</item>\n        <item name=\"android:textSize\">15sp</item>\n        <item name=\"android:textColor\">?attr/qmui_skin_support_dialog_menu_item_text_color</item>\n        <item name=\"android:gravity\">left|center_vertical</item>\n        <item name=\"qmui_dialog_menu_item_mark_drawable\">\n            ?attr/qmui_skin_support_dialog_mark_drawable\n        </item>\n        <item name=\"qmui_dialog_menu_item_check_drawable\">\n            ?attr/qmui_skin_support_s_dialog_check_drawable\n        </item>\n        <item name=\"qmui_dialog_menu_item_check_mark_margin_hor\">6dp</item>\n    </style>\n\n    <style name=\"QMUI.BottomSheet.Title\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_bottom_sheet_title_text_color</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingTop\">14dp</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_bottom_sheet_padding_hor</item>\n        <item name=\"android:paddingRight\">?attr/qmui_bottom_sheet_padding_hor</item>\n        <item name=\"android:paddingBottom\">18dp</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:lineSpacingExtra\">2dp</item>\n    </style>\n\n    <style name=\"QMUI.BottomSheet.Cancel\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_bottom_sheet_cancel_text_color</item>\n        <item name=\"android:textSize\">16sp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_bottom_sheet_padding_hor</item>\n        <item name=\"android:paddingRight\">?attr/qmui_bottom_sheet_padding_hor</item>\n    </style>\n\n    <style name=\"QMUI.BottomSheet.List\" />\n\n    <style name=\"QMUI.BottomSheet.List.Text\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_bottom_sheet_list_item_text_color\n        </item>\n        <item name=\"android:textSize\">16sp</item>\n        <item name=\"android:ellipsize\">middle</item>\n        <item name=\"android:lineSpacingExtra\">2dp</item>\n    </style>\n\n    <style name=\"QMUI.BottomSheet.Grid\" />\n\n    <style name=\"QMUI.BottomSheet.Grid.Text\">\n        <item name=\"android:textColor\">?attr/qmui_skin_support_bottom_sheet_grid_item_text_color\n        </item>\n        <item name=\"android:textSize\">11sp</item>\n        <item name=\"android:ellipsize\">middle</item>\n        <item name=\"android:lineSpacingExtra\">2dp</item>\n        <item name=\"android:paddingLeft\">4dp</item>\n        <item name=\"android:paddingRight\">4dp</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <!--********************* Tip *********************-->\n    <!-- 提示更新小圆点 -->\n    <style name=\"QMUI.TipPoint\">\n        <item name=\"android:layout_width\">@dimen/qmui_tips_point_size</item>\n        <item name=\"android:layout_height\">@dimen/qmui_tips_point_size</item>\n        <item name=\"android:background\">@drawable/qmui_tips_point</item>\n    </style>\n\n    <!-- 提示更新new -->\n    <style name=\"QMUI.TipNew\">\n        <item name=\"android:layout_width\">37dp</item>\n        <item name=\"android:layout_height\">17dp</item>\n        <item name=\"android:src\">@drawable/qmui_icon_tip_new</item>\n    </style>\n\n    <style name=\"QMUI.Slider\">\n        <item name=\"qmui_slider_bar_height\">2dp</item>\n        <item name=\"qmui_slider_bar_normal_color\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_slider_bar_progress_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_slider_bar_tick_count\">100</item>\n        <item name=\"qmui_slider_bar_thumb_size\">20dp</item>\n        <item name=\"qmui_slider_bar_use_clip_children_by_developer\">false</item>\n        <item name=\"qmui_slider_bar_padding_hor_for_thumb_shadow\">8dp</item>\n        <item name=\"qmui_slider_bar_padding_ver_for_thumb_shadow\">8dp</item>\n        <item name=\"qmui_slider_bar_thumb_style_attr\">QMUISliderThumbStyle</item>\n        <item name=\"qmui_slider_bar_constraint_thumb_in_moving\">false</item>\n    </style>\n\n    <style name=\"QMUI.SeekBar\">\n        <item name=\"qmui_slider_bar_height\">1dp</item>\n        <item name=\"qmui_slider_bar_normal_color\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_slider_bar_progress_color\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_slider_bar_tick_count\">10</item>\n        <item name=\"qmui_slider_bar_thumb_size\">20dp</item>\n        <item name=\"qmui_slider_bar_use_clip_children_by_developer\">false</item>\n        <item name=\"qmui_slider_bar_padding_hor_for_thumb_shadow\">8dp</item>\n        <item name=\"qmui_slider_bar_padding_ver_for_thumb_shadow\">8dp</item>\n        <item name=\"qmui_slider_bar_thumb_style_attr\">QMUISliderThumbStyle</item>\n        <item name=\"qmui_slider_bar_constraint_thumb_in_moving\">true</item>\n        <item name=\"qmui_seek_bar_tick_height\">6dp</item>\n        <item name=\"qmui_seek_bar_tick_width\">2dp</item>\n    </style>\n\n    <style name=\"QMUI.SliderThumb\">\n        <item name=\"android:background\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_borderColor\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_borderWidth\">1px</item>\n        <item name=\"qmui_showBorderOnlyBeforeL\">false</item>\n        <item name=\"qmui_shadowElevation\">8dp</item>\n        <item name=\"qmui_shadowAlpha\">0.4</item>\n    </style>\n\n    <!-- QMUIPullRefreshLayout -->\n    <style name=\"QMUI.PullRefreshLayout\">\n        <item name=\"qmui_target_init_offset\">0dp</item>\n        <item name=\"qmui_target_refresh_offset\">72dp</item>\n        <item name=\"qmui_auto_calculate_refresh_init_offset\">true</item>\n        <item name=\"qmui_auto_calculate_refresh_end_offset\">true</item>\n    </style>\n\n    <!-- QMUIPullLayout -->\n    <style name=\"QMUI.PullLayout\">\n        <item name=\"qmui_pull_enable_edge\">left|right|top|bottom</item>\n    </style>\n\n    <!-- QMUIPullLoadMoreView -->\n    <style name=\"QMUI.PullLoadMore\">\n        <item name=\"qmui_pull_load_more_loading_size\">20dp</item>\n        <item name=\"qmui_pull_load_more_text_size\">14sp</item>\n        <item name=\"qmui_pull_load_more_arrow_text_gap\">10dp</item>\n        <item name=\"qmui_pull_load_more_height\">56dp</item>\n        <item name=\"qmui_pull_load_more_arrow\">@drawable/qmui_icon_pull_down</item>\n        <item name=\"qmui_pull_load_more_pull_text\">上拉加载更多</item>\n        <item name=\"qmui_pull_load_more_release_text\">松手加载更多</item>\n        <item name=\"qmui_skin_support_pull_load_more_bg_color\">\n            ?attr/qmui_skin_support_pull_load_more_bg_color\n        </item>\n        <item name=\"qmui_skin_support_pull_load_more_loading_tint_color\">\n            ?attr/qmui_skin_support_pull_load_more_loading_tint_color\n        </item>\n        <item name=\"qmui_skin_support_pull_load_more_arrow_tint_color\">\n            ?attr/qmui_skin_support_pull_load_more_arrow_tint_color\n        </item>\n        <item name=\"qmui_skin_support_pull_load_more_text_color\">\n            ?attr/qmui_skin_support_pull_load_more_text_color\n        </item>\n    </style>\n\n    <!-- QMUIRadiusImageView -->\n    <style name=\"QMUI.RadiusImageView\">\n        <item name=\"qmui_border_width\">1px</item>\n        <item name=\"qmui_border_color\">?attr/qmui_config_color_gray_4</item>\n        <item name=\"qmui_is_touch_select_mode_enabled\">true</item>\n    </style>\n\n    <!-- QMUIQQFaceView -->\n    <style name=\"QMUI.QQFaceView\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_black</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"qmui_special_drawable_padding\">5dp</item>\n    </style>\n\n    <style name=\"QMUI.Animation\" parent=\"@android:style/Animation\" />\n\n    <style name=\"QMUI.Animation.Scale\">\n        <item name=\"android:windowEnterAnimation\">@anim/scale_in_center</item>\n        <item name=\"android:windowExitAnimation\">@anim/scale_out_center</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopUpMenu\" />\n\n    <style name=\"QMUI.Animation.PopUpMenu.Center\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_bottom</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_top</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopUpMenu.Left\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_bottomleft_to_topright</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_topright_to_bottomleft</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopUpMenu.Right\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_bottomright_to_topleft</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_topleft_to_bottomright</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopDownMenu\" />\n\n    <style name=\"QMUI.Animation.PopDownMenu.Center\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_top</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_bottom</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopDownMenu.Left\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_topleft_to_bottomright</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_bottomright_to_topleft</item>\n    </style>\n\n    <style name=\"QMUI.Animation.PopDownMenu.Right\">\n        <item name=\"android:windowEnterAnimation\">@anim/grow_from_topright_to_bottomleft</item>\n        <item name=\"android:windowExitAnimation\">@anim/shrink_from_bottomleft_to_topright</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values/qmui_themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <!-- 关于Theme的继承关系：\n    由于 Theme 需要在不同 API Level 下有不同的配置，并且继承自不同的系统 Theme，所以多层继承。\n\n    继承关系和每个层的分工如下：\n\tAppRootTheme（Theme 的最顶层，各个 API Level 自行决定继承自系统的哪个 Theme）\n\t  |-values\n\t  |-values-v21\n\t  |-values-...\n\t  |\n\tAppBaseTheme（基础 Theme，定义所有公用的 style。）\n\t  |-values\n\t  |\n\tAppConfigTheme（可配置的 Theme，各个 API Level 自行配置各自的 style 差异。）\n\t  |-values\n\t  |-values-v21\n\t  |-values-...\n\t  |\n\tQMUI（QMUI提供的theme控制， 供外界使用）\n\n\tSo，\n\t如果要对某 API Level 配置继承自某个系统的 Theme，在 values-v* 目录下修改 qmui_themes.xml 的 AppRootTheme。\n\t如果要对某 API Level 配置定制化的 Theme，在 values-v* 目录下修改 qmui_themes.xml 的 AppConfigTheme。\n\t如果在项目中要对特定项目配置业务相关的 Theme，在 values 目录下修改 themes.xml 的 AppTheme。\n     -->\n\n    <style name=\"AppRootTheme\" parent=\"Theme.AppCompat.DayNight\" />\n\n    <style name=\"AppBaseTheme\" parent=\"AppRootTheme\">\n        <item name=\"android:listDivider\">@drawable/qmui_divider</item>\n        <item name=\"android:listViewStyle\">@style/ListView.Compat</item>\n        <item name=\"android:gridViewStyle\">@style/GridView.Compat</item>\n        <item name=\"android:imageButtonStyle\">@style/ImageButton.Compat</item>\n        <item name=\"android:buttonStyle\">@style/Button.Compat</item>\n        <item name=\"android:editTextStyle\">@style/EditText.Compat</item>\n        <item name=\"android:autoCompleteTextViewStyle\">@style/AutoCompleteTextView.Compat</item>\n        <item name=\"android:dropDownListViewStyle\">@style/DropDownListView.Compat</item>\n        <item name=\"android:textViewStyle\">@style/TextView.Compat</item>\n\n        <item name=\"imageButtonStyle\">@style/ImageButton.Compat</item>\n        <item name=\"buttonStyle\">@style/Button.Compat</item>\n        <item name=\"editTextStyle\">@style/EditText.Compat</item>\n        <item name=\"autoCompleteTextViewStyle\">@style/AutoCompleteTextView.Compat</item>\n        <item name=\"dropDownListViewStyle\">@style/DropDownListView.Compat</item>\n    </style>\n\n\n    <style name=\"AppConfigTheme\" parent=\"AppBaseTheme\">\n        <item name=\"colorPrimary\">?attr/qmui_config_color_blue</item>\n        <item name=\"colorPrimaryDark\">?attr/qmui_config_color_blue</item>\n        <item name=\"colorAccent\">?attr/qmui_config_color_blue</item>\n        <item name=\"colorControlNormal\">@color/qmui_config_color_white</item>\n        <item name=\"android:textColorPrimary\">?attr/qmui_config_color_blue</item>\n        <item name=\"android:textColorSecondary\">?attr/qmui_config_color_blue</item>\n        <item name=\"android:windowBackground\">?attr/qmui_skin_support_activity_background</item>\n        <item name=\"android:editTextColor\">?attr/qmui_config_color_black</item>\n    </style>\n\n    <style name=\"QMUI\" parent=\"AppConfigTheme\">\n\n        <!--***************************** qmui topbar  ***************************** -->\n        <item name=\"qmui_topbar_height\">56dp</item>\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_topbar_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_topbar_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_topbar_title_color\">@color/qmui_config_color_gray_1</item>\n        <item name=\"qmui_skin_support_topbar_subtitle_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_topbar_text_btn_color_state_list\">@color/qmui_config_color_gray_1</item>\n        <item name=\"qmui_skin_support_topbar_image_tint_color\">@color/qmui_config_color_gray_1</item>\n        <!-- skin support-->\n\n        <!--*************************** qmui tabSegment  *************************** -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_tab_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tab_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_tab_normal_color\">?attr/qmui_config_color_gray_6</item>\n        <item name=\"qmui_skin_support_tab_selected_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_tab_sign_count_view_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tab_sign_count_view_bg_color\">?attr/qmui_config_color_red</item>\n        <!-- skin support-->\n\n\n        <item name=\"qmui_tab_sign_count_view\">@style/QMUI.TabSegment.SignCount</item>\n        <item name=\"qmui_tab_sign_count_view_min_size\">@dimen/qmui_tab_sign_count_view_min_size</item>\n        <item name=\"qmui_tab_sign_count_view_min_size_with_text\">\n            @dimen/qmui_tab_sign_count_view_min_size_with_text\n        </item>\n        <item name=\"qmui_tab_sign_count_view_padding_horizontal\">4dp</item>\n\n        <!--****************************** qmui btn  ******************************* -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_round_btn_bg_color\">@color/qmui_s_transparent</item>\n        <item name=\"qmui_skin_support_round_btn_border_color\">@color/qmui_btn_blue_border</item>\n        <item name=\"qmui_skin_support_round_btn_text_color\">@color/qmui_btn_blue_text</item>\n        <!-- skin support-->\n\n        <item name=\"qmui_round_btn_text_size\">@dimen/qmui_btn_text_size</item>\n        <item name=\"qmui_round_btn_border_width\">@dimen/qmui_btn_border_width</item>\n\n        <!--************************ qmui loading view   *************************** -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_loading_color\">@color/qmui_config_color_gray_5</item>\n        <!-- skin support-->\n\n        <item name=\"qmui_loading_size\">20dp</item>\n\n\n        <!--********************* qmui pull load more view   ************************ -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_pull_load_more_bg_color\">@color/qmui_config_color_transparent</item>\n        <item name=\"qmui_skin_support_pull_load_more_loading_tint_color\">?attr/qmui_skin_support_loading_color</item>\n        <item name=\"qmui_skin_support_pull_load_more_arrow_tint_color\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_pull_load_more_text_color\">@color/qmui_config_color_gray_3</item>\n        <!-- skin support-->\n\n\n        <!--************************ qmui empty view   *************************** -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_empty_view_loading_color\">?attr/qmui_skin_support_loading_color</item>\n        <item name=\"qmui_skin_support_empty_view_title_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_empty_view_sub_title_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_empty_view_btn_bg_color\">?attr/qmui_skin_support_round_btn_bg_color</item>\n        <item name=\"qmui_skin_support_empty_view_btn_border_color\">?attr/qmui_skin_support_round_btn_border_color</item>\n        <item name=\"qmui_skin_support_empty_view_btn_text_color\">?attr/qmui_skin_support_round_btn_text_color</item>\n        <!-- skin support-->\n\n        <item name=\"qmui_empty_view_loading_size\">?attr/qmui_loading_size</item>\n        <item name=\"qmui_empty_view_title_normal_margin_top\">16dp</item>\n        <item name=\"qmui_empty_view_title_text_size\">16sp</item>\n        <item name=\"qmui_empty_view_title_margin_hor\">?attr/qmui_content_spacing_horizontal</item>\n        <item name=\"qmui_empty_view_sub_title_normal_margin_top\">16dp</item>\n        <item name=\"qmui_empty_view_sub_title_text_size\">14sp</item>\n        <item name=\"qmui_empty_view_sub_title_margin_hor\">?attr/qmui_content_spacing_horizontal\n        </item>\n        <item name=\"qmui_empty_view_btn_height\">48dp</item>\n        <item name=\"qmui_empty_view_btn_margin_hor\">55dp</item>\n        <item name=\"qmui_empty_view_btn_text_size\">15sp</item>\n        <item name=\"qmui_empty_view_btn_normal_margin_top\">10dp</item>\n\n\n        <!--************************ qmui common list   *************************** -->\n\n        <!-- skin support-->\n        <item name=\"qmui_skin_support_common_list_title_color\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"qmui_skin_support_common_list_detail_color\">?attr/qmui_config_color_gray_5</item>\n        <item name=\"qmui_skin_support_common_list_icon_tint_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_common_list_red_point_tint_color\">?attr/qmui_config_color_red</item>\n        <item name=\"qmui_skin_support_common_list_new_drawable\">@drawable/qmui_icon_tip_new</item>\n        <item name=\"qmui_skin_support_common_list_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_s_common_list_bg\">?attr/qmui_skin_support_s_list_item_bg_2</item>\n        <item name=\"qmui_skin_support_common_list_chevron_color\">?attr/qmui_config_color_gray_5</item>\n        <!-- skin support-->\n\n        <item name=\"qmui_common_list_item_icon_margin_right\">12dp</item>\n        <item name=\"qmui_common_list_item_accessory_margin_left\">14dp</item>\n        <item name=\"qmui_common_list_item_title_v_text_size\">15sp</item>\n        <item name=\"qmui_common_list_item_title_h_text_size\">16sp</item>\n        <item name=\"qmui_common_list_item_title_line_space\">5dp</item>\n        <item name=\"qmui_common_list_item_detail_v_text_size\">13sp</item>\n        <item name=\"qmui_common_list_item_detail_h_text_size\">14sp</item>\n        <item name=\"qmui_common_list_item_detail_line_space\">5dp</item>\n        <item name=\"qmui_common_list_item_detail_h_margin_with_title\">20dp</item>\n        <item name=\"qmui_common_list_item_detail_v_margin_with_title\">4dp</item>\n        <item name=\"qmui_common_list_item_holder_margin_with_title\">8dp</item>\n        <item name=\"qmui_common_list_item_chevron\">@drawable/qmui_icon_chevron</item>\n        <item name=\"qmui_common_list_item_switch\">@drawable/qmui_s_icon_switch</item>\n\n\n        <item name=\"qmui_content_padding_horizontal\">@dimen/qmui_content_padding_horizontal\n        </item> <!-- 已废弃 -->\n        <item name=\"qmui_content_spacing_horizontal\">@dimen/qmui_content_spacing_horizontal</item>\n\n        <!--**********************************************\n        *                qmui common color               *\n        **********************************************-->\n        <item name=\"qmui_config_color_blue\">@color/qmui_config_color_blue</item>\n        <item name=\"qmui_config_color_red\">@color/qmui_config_color_red</item>\n\n        <item name=\"qmui_config_color_black\">@color/qmui_config_color_black</item>\n        <item name=\"qmui_config_color_link\">@color/qmui_config_color_link</item>\n        <item name=\"qmui_config_color_pressed\">@color/qmui_config_color_pressed</item>\n\n        <item name=\"qmui_config_color_gray_1\">@color/qmui_config_color_gray_1</item>\n        <item name=\"qmui_config_color_gray_2\">@color/qmui_config_color_gray_2</item>\n        <item name=\"qmui_config_color_gray_3\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_config_color_gray_4\">@color/qmui_config_color_gray_4</item>\n        <item name=\"qmui_config_color_gray_5\">@color/qmui_config_color_gray_5</item>\n        <item name=\"qmui_config_color_gray_6\">@color/qmui_config_color_gray_6</item>\n        <item name=\"qmui_config_color_gray_7\">@color/qmui_config_color_gray_7</item>\n        <item name=\"qmui_config_color_gray_8\">@color/qmui_config_color_gray_8</item>\n        <item name=\"qmui_config_color_gray_9\">@color/qmui_config_color_gray_9</item>\n\n        <item name=\"qmui_alpha_pressed\">0.5</item>\n        <item name=\"qmui_alpha_disabled\">0.5</item>\n\n        <item name=\"qmui_general_shadow_elevation\">14dp</item>\n        <item name=\"qmui_general_shadow_alpha\">0.25</item>\n\n        <item name=\"qmui_skin_support_activity_background\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_color_separator\">@color/qmui_config_color_separator</item>\n        <item name=\"qmui_skin_support_color_separator_darken\">@color/qmui_config_color_separator_darken</item>\n        <item name=\"qmui_skin_support_color_background\">@color/qmui_config_color_background</item>\n        <item name=\"qmui_skin_support_color_background_pressed\">@color/qmui_config_color_background_pressed</item>\n        <item name=\"qmui_skin_support_pull_refresh_view_color\">?attr/qmui_config_color_blue</item>\n\n        <!--**********************************************\n        *                qmui dialog                     *\n        **********************************************-->\n        <item name=\"qmui_dialog_min_width\">260dp</item>\n        <item name=\"qmui_dialog_max_width\">320dp</item>\n        <item name=\"qmui_dialog_radius\">@dimen/qmui_dialog_radius</item>\n        <item name=\"qmui_dialog_inset_hor\">40dp</item>\n        <item name=\"qmui_dialog_inset_ver\">20dp</item>\n        <item name=\"qmui_dialog_background_dim_amount\">0.6</item>\n        <item name=\"qmui_dialog_padding_horizontal\">24dp</item>\n        <item name=\"qmui_dialog_edit_margin_top\">20dp</item>\n        <item name=\"qmui_dialog_edit_margin_bottom\">24dp</item>\n        <item name=\"qmui_dialog_edit_bottom_line_height\">1dp</item>\n        <item name=\"qmui_dialog_title_style\">@style/QMUI.Dialog.Title</item>\n        <item name=\"qmui_dialog_message_content_style\">@style/QMUI.Dialog.MessageContent</item>\n        <item name=\"qmui_dialog_action_container_style\">@style/QMUI.Dialog.ActionContainer</item>\n        <item name=\"qmui_dialog_action_style\">@style/QMUI.Dialog.Action</item>\n        <item name=\"qmui_dialog_edit_content_style\">@style/QMUI.Dialog.EditContent</item>\n        <item name=\"qmui_dialog_menu_container_style\">@style/QMUI.Dialog.MenuContainer</item>\n        <item name=\"qmui_dialog_menu_item_style\">@style/QMUI.Dialog.MenuItem</item>\n\n\n        <item name=\"qmui_skin_support_dialog_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_dialog_title_text_color\">?attr/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_dialog_message_text_color\">?attr/qmui_config_color_gray_4</item>\n        <item name=\"qmui_skin_support_dialog_action_bg\">@null</item>\n        <item name=\"qmui_skin_support_dialog_action_text_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_dialog_action_container_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_dialog_positive_action_text_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_dialog_negative_action_text_color\">?attr/qmui_config_color_red</item>\n        <item name=\"qmui_skin_support_dialog_action_divider_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_dialog_edit_bottom_line_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_dialog_edit_text_color\">?attr/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_dialog_edit_text_hint_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_s_dialog_menu_item_bg\">?attr/qmui_skin_support_s_list_item_bg_1</item>\n        <item name=\"qmui_skin_support_dialog_menu_item_text_color\">?attr/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_s_dialog_check_drawable\">?attr/qmui_skin_support_s_checkbox</item>\n        <item name=\"qmui_skin_support_dialog_mark_drawable\">?attr/qmui_skin_support_icon_mark</item>\n\n\n        <!--**********************************************\n        *             qmui tip dialog                    *\n        **********************************************-->\n        <item name=\"qmui_tip_dialog_min_width\">120dp</item>\n        <item name=\"qmui_tip_dialog_min_height\">40dp</item>\n        <item name=\"qmui_tip_dialog_max_width\">270dp</item>\n        <item name=\"qmui_tip_dialog_padding_vertical\">14dp</item>\n        <item name=\"qmui_tip_dialog_padding_horizontal\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"qmui_tip_dialog_radius\">15dp</item>\n        <item name=\"qmui_tip_dialog_loading_size\">32dp</item>\n        <item name=\"qmui_tip_dialog_text_margin_top\">12dp</item>\n        <item name=\"qmui_tip_dialog_text_size\">14sp</item>\n\n        <item name=\"qmui_skin_support_tip_dialog_bg\">@color/qmui_config_color_75_pure_black</item>\n        <item name=\"qmui_skin_support_tip_dialog_loading_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tip_dialog_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tip_dialog_icon_success_src\">@drawable/qmui_icon_notify_done</item>\n        <item name=\"qmui_skin_support_tip_dialog_icon_error_src\">@drawable/qmui_icon_notify_error</item>\n        <item name=\"qmui_skin_support_tip_dialog_icon_info_src\">@drawable/qmui_icon_notify_info</item>\n\n        <!--**********************************************\n        *             qmui list item bg                  *\n        **********************************************-->\n        <item name=\"qmui_list_item_height\">@dimen/qmui_list_item_height</item>\n        <item name=\"qmui_list_item_height_higher\">@dimen/qmui_list_item_height_higher</item>\n\n        <item name=\"qmui_skin_support_s_list_item_bg_1\">@drawable/qmui_s_list_item_bg_1</item>\n        <item name=\"qmui_skin_support_s_list_item_bg_2\">@drawable/qmui_s_list_item_bg_2</item>\n\n\n        <!--**********************************************\n        *             qmui drawable                      *\n        ***********************************************-->\n        <item name=\"qmui_skin_support_s_checkbox\">@drawable/qmui_s_checkbox</item>\n        <item name=\"qmui_skin_support_icon_mark\">@drawable/qmui_icon_checkmark</item>\n\n        <!--**********************************************\n        *               qmui slider                      *\n        ***********************************************-->\n        <item name=\"qmui_skin_support_slider_bar_bg_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_slider_bar_progress_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_slider_record_progress_color\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"qmui_skin_support_slider_thumb_bg_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_slider_thumb_border_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_seek_bar_color\">?attr/qmui_config_color_gray_3</item>\n\n\n        <!--**********************************************\n        *                 qmui bottom sheet              *\n        ***********************************************-->\n        <item name=\"qmui_skin_support_bottom_sheet_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_bottom_sheet_title_text_color\">?attr/qmui_config_color_gray_5</item>\n        <item name=\"qmui_skin_support_bottom_sheet_cancel_text_color\">?attr/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_bottom_sheet_cancel_bg\">?attr/qmui_skin_support_s_list_item_bg_1</item>\n        <item name=\"qmui_skin_support_bottom_sheet_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_bottom_sheet_list_item_bg\">?attr/qmui_skin_support_s_list_item_bg_1</item>\n        <item name=\"qmui_skin_support_bottom_sheet_list_item_text_color\">?attr/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_bottom_sheet_list_red_point_color\">?attr/qmui_config_color_red</item>\n        <item name=\"qmui_skin_support_bottom_sheet_list_mark\">?attr/qmui_skin_support_icon_mark</item>\n        <item name=\"qmui_skin_support_bottom_sheet_grid_item_text_color\">?attr/qmui_config_color_gray_3</item>\n\n        <item name=\"qmui_bottom_sheet_radius\">12dp</item>\n        <item name=\"qmui_bottom_sheet_padding_hor\">?attr/qmui_content_padding_horizontal</item>\n        <item name=\"qmui_bottom_sheet_use_percent_min_height\">640dp</item>\n        <item name=\"qmui_bottom_sheet_height_percent\">0.75</item>\n        <item name=\"qmui_bottom_sheet_max_width\">500dp</item>\n        <item name=\"qmui_bottom_sheet_background_dim_amount\">0.6</item>\n        <item name=\"qmui_bottom_sheet_cancel_btn_height\">56dp</item>\n        <item name=\"qmui_bottom_sheet_list_item_height\">56dp</item>\n        <item name=\"qmui_bottom_sheet_list_item_separator_height\">1px</item>\n        <item name=\"qmui_bottom_sheet_list_item_icon_size\">22dp</item>\n        <item name=\"qmui_bottom_sheet_list_item_red_point_size\">@dimen/qmui_tips_point_size</item>\n        <item name=\"qmui_bottom_sheet_list_item_icon_margin_right\">12dp</item>\n        <item name=\"qmui_bottom_sheet_list_item_mark_margin_left\">12dp</item>\n        <item name=\"qmui_bottom_sheet_list_item_tip_point_margin_left\">4dp</item>\n        <item name=\"qmui_bottom_sheet_grid_padding_top\">12dp</item>\n        <item name=\"qmui_bottom_sheet_grid_padding_bottom\">12dp</item>\n        <item name=\"qmui_bottom_sheet_grid_line_vertical_space\">0dp</item>\n        <item name=\"qmui_bottom_sheet_grid_item_padding_top\">12dp</item>\n        <item name=\"qmui_bottom_sheet_grid_item_padding_bottom\">12dp</item>\n        <item name=\"qmui_bottom_sheet_grid_item_text_margin_top\">9dp</item>\n        <item name=\"qmui_bottom_sheet_grid_item_mini_width\">84dp</item>\n        <item name=\"qmui_bottom_sheet_grid_item_icon_size\">56dp</item>\n\n        <item name=\"qmui_bottom_sheet_title_style\">@style/QMUI.BottomSheet.Title</item>\n        <item name=\"qmui_bottom_sheet_cancel_style\">@style/QMUI.BottomSheet.Cancel</item>\n        <item name=\"qmui_bottom_sheet_list_item_text_style\">@style/QMUI.BottomSheet.List.Text</item>\n        <item name=\"qmui_bottom_sheet_grid_item_text_style\">@style/QMUI.BottomSheet.Grid.Text</item>\n\n\n        <!--**********************************************\n        *                   qmui popup                   *\n        ***********************************************-->\n        <item name=\"qmui_popup_radius\">12dp</item>\n        <item name=\"qmui_popup_border_width\">1px</item>\n        <item name=\"qmui_popup_shadow_elevation\">16dp</item>\n        <item name=\"qmui_popup_shadow_alpha\">0.25</item>\n        <item name=\"qmui_popup_shadow_inset\">30dp</item>\n        <item name=\"qmui_popup_arrow_width\">18dp</item>\n        <item name=\"qmui_popup_arrow_height\">10dp</item>\n        <item name=\"qmui_quick_action_item_padding_hor\">4dp</item>\n        <item name=\"qmui_quick_action_item_padding_ver\">2dp</item>\n        <item name=\"qmui_quick_action_item_middle_space\">4dp</item>\n        <item name=\"qmui_quick_action_padding_hor\">4dp</item>\n        <item name=\"qmui_quick_action_more_arrow_width\">36dp</item>\n\n        <item name=\"qmui_skin_support_popup_border_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_popup_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_popup_close_icon\">@drawable/qmui_icon_popup_close_with_bg</item>\n        <item name=\"qmui_skin_support_quick_action_item_tint_color\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"qmui_skin_support_quick_action_more_left_arrow\">@drawable/qmui_icon_quick_action_more_arrow_left</item>\n        <item name=\"qmui_skin_support_quick_action_more_right_arrow\">@drawable/qmui_icon_quick_action_more_arrow_right</item>\n        <item name=\"qmui_skin_support_quick_action_more_tint_color\">?attr/qmui_skin_support_quick_action_item_tint_color</item>\n\n\n        <!--**********************************************\n        *               qmui 提供的控件样式                *\n        ***********************************************-->\n        <item name=\"QMUIButtonStyle\">@style/QMUI.RoundButton</item>\n        <item name=\"QMUITabSegmentStyle\">@style/QMUI.TabSegment</item>\n        <item name=\"QMUICommonListItemViewStyle\">@style/QMUI.CommonListItemView</item>\n        <item name=\"QMUIGroupListSectionViewStyle\">@style/QMUI.GroupListSectionView</item>\n        <item name=\"QMUITopBarStyle\">@style/QMUI.TopBar</item>\n\n        <item name=\"QMUITipPointStyle\">@style/QMUI.TipPoint</item>\n        <item name=\"QMUITipNewStyle\">@style/QMUI.TipNew</item>\n\n        <item name=\"QMUILoadingStyle\">@style/QMUI.Loading</item>\n        <item name=\"QMUIPullRefreshLayoutStyle\">@style/QMUI.PullRefreshLayout</item>\n        <item name=\"QMUIRadiusImageViewStyle\">@style/QMUI.RadiusImageView</item>\n        <item name=\"QMUIQQFaceStyle\">@style/QMUI.QQFaceView</item>\n        <item name=\"QMUISliderStyle\">@style/QMUI.Slider</item>\n        <item name=\"QMUISeekBarStyle\">@style/QMUI.SeekBar</item>\n        <item name=\"QMUISliderThumbStyle\">@style/QMUI.SliderThumb</item>\n        <item name=\"QMUIPullLayoutStyle\">@style/QMUI.PullLayout</item>\n        <item name=\"QMUIPullLoadMoreStyle\">@style/QMUI.PullLoadMore</item>\n    </style>\n\n    <style name=\"QMUI.Compat\" parent=\"QMUI\" />\n\n    <style name=\"ListViewBase.Compat\" parent=\"@style/Widget.AppCompat.ListView\" />\n\n    <style name=\"GridViewBase.Compat\" parent=\"android:Widget.GridView\" />\n\n    <style name=\"ImageButtonBase.Compat\" parent=\"@style/Widget.AppCompat.ImageButton\" />\n\n    <style name=\"ButtonBase.Compat\" parent=\"@style/Widget.AppCompat.Button\">\n        <!-- 取消 Button 字符默认大写的功能（会导致 Spannable 失效），默认值只影响 5.0 以上版本，但使用Compat包后，4.x 版本也会受到影响 -->\n        <!-- http://stackoverflow.com/questions/29007746/button-settext-with-spannable-dosent-work-for-android-5-0-lollipop -->\n        <item name=\"android:textAllCaps\">false</item>\n    </style>\n\n    <style name=\"EditTextBase.Compat\" parent=\"@style/Widget.AppCompat.EditText\" />\n\n    <style name=\"AutoCompleteTextViewBase.Compat\" parent=\"@style/Widget.AppCompat.AutoCompleteTextView\" />\n\n    <style name=\"DropDownListViewBase.Compat\" parent=\"@style/Widget.AppCompat.ListView.DropDown\" />\n\n    <style name=\"TextAppearanceBase\" parent=\"android:TextAppearance\" />\n\n    <style name=\"QMUI.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n    <style name=\"QMUI.Compat.NoActionBar\" parent=\"QMUI.NoActionBar\" />\n\n    <!-- ListView Start -->\n    <style name=\"ListView.Compat\" parent=\"ListViewBase.Compat\">\n        <item name=\"android:background\">@color/qmui_config_color_transparent</item>\n        <item name=\"android:listSelector\">@color/qmui_config_color_transparent</item>\n        <item name=\"android:divider\">@drawable/qmui_divider_bottom_bitmap</item>\n        <item name=\"android:dividerHeight\">@dimen/qmui_list_divider_height</item>\n    </style>\n    <!-- ListView End -->\n\n    <!-- GridView -->\n    <style name=\"GridView.Compat\" parent=\"GridViewBase.Compat\">\n        <item name=\"android:background\">@color/qmui_config_color_transparent</item>\n        <item name=\"android:listSelector\">@color/qmui_config_color_transparent</item>\n    </style>\n\n    <style name=\"ImageButton.Compat\" parent=\"ImageButtonBase.Compat\">\n        <item name=\"android:background\">@color/qmui_config_color_transparent</item>\n    </style>\n\n    <style name=\"Button.Compat\" parent=\"ButtonBase.Compat\" />\n\n    <style name=\"EditText.Compat\" parent=\"EditTextBase.Compat\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"android:textColorHint\">?attr/qmui_config_color_gray_4</item>\n        <!-- 光标颜色与textColor一致，若要自定义，应该用ShapeDrawable，不能简单指定一个color -->\n        <item name=\"android:textCursorDrawable\">@null</item>\n    </style>\n\n    <style name=\"TextView.Compat\" parent=\"android:Widget.TextView\">\n        <item name=\"android:textAppearance\">@style/QMUITextAppearance</item>\n        <!-- textColorHighlight 放在appearance中无效，好像TextView总是默认存在一个textColorHighlight\n             会覆盖掉它，并不知道原因 -->\n        <item name=\"android:textColorHighlight\">?attr/qmui_skin_support_color_background_pressed</item>\n    </style>\n\n    <style name=\"AutoCompleteTextView.Compat\" parent=\"AutoCompleteTextViewBase.Compat\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"android:textColorHint\">?attr/qmui_config_color_gray_4</item>\n        <!-- 光标颜色与textColor一致，若要自定义，应该用ShapeDrawable，不能简单指定一个color -->\n        <item name=\"android:textCursorDrawable\">@null</item>\n    </style>\n\n    <!-- 下拉列表，例如AutoCompleteTextView的补全列表 -->\n    <style name=\"DropDownListView.Compat\" parent=\"DropDownListViewBase.Compat\">\n        <item name=\"android:divider\">@drawable/qmui_divider_bottom_bitmap</item>\n        <item name=\"android:dividerHeight\">@dimen/qmui_list_divider_height</item>\n        <item name=\"android:background\">@color/qmui_config_color_white</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "qmui/src/main/res/values-v21/qmui_themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <style name=\"AppConfigTheme\" parent=\"AppBaseTheme\" >\n        <item name=\"android:navigationBarColor\">?attr/qmui_config_color_black</item>\n        <!-- 该行会影响顶部条收起时效果,为false时，状态栏颜色不会被改变 -->\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n    </style>\n\n    <style name=\"Button.Compat\" parent=\"ButtonBase.Compat\">\n        <!-- 去掉5.0下Button点击的阴影动画效果 -->\n        <item name=\"android:stateListAnimator\">@null</item>\n    </style>\n</resources>"
  },
  {
    "path": "qmuidemo/.gitignore",
    "content": ".idea\n.DS_Store\nlocal.properties\n/*.iml\n/build\nrelease.properties\nqmuidemo.keystore\n"
  },
  {
    "path": "qmuidemo/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\nimport java.io.ByteArrayOutputStream\nimport java.util.*\n\nplugins {\n    id(\"com.android.application\")\n    kotlin(\"android\")\n    kotlin(\"kapt\")\n}\n\nfun runCommand(project: Project, command: String): String {\n    val stdout = ByteArrayOutputStream()\n    project.exec {\n        commandLine = command.split(\" \")\n        standardOutput = stdout\n    }\n    return stdout.toString().trim()\n}\n\n\nval gitVersion = runCommand(project, \"git rev-list HEAD --count\").toIntOrNull() ?: 1\n\n\nandroid {\n    signingConfigs {\n        val properties = Properties()\n        val propFile = project.file(\"release.properties\")\n        if (propFile.exists()) {\n            properties.load(propFile.inputStream())\n        }\n        create(\"release\"){\n            keyAlias = properties.getProperty(\"RELEASE_KEY_ALIAS\")\n            keyPassword = properties.getProperty(\"RELEASE_KEY_PASSWORD\")\n            storeFile = file(\"qmuidemo.keystore\")\n            storePassword = properties.getProperty(\"RELEASE_STORE_PASSWORD\")\n            enableV2Signing = true\n        }\n    }\n\n    compileSdk = Dep.compileSdk\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n        freeCompilerArgs += \"-Xjvm-default=all\"\n    }\n\n    buildFeatures {\n        compose = true\n        buildConfig = true\n    }\n\n    composeOptions {\n        kotlinCompilerExtensionVersion = Dep.Compose.version\n    }\n\n    defaultConfig {\n        applicationId = \"com.qmuiteam.qmuidemo\"\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n        versionCode = gitVersion\n        versionName = Dep.QMUI.qmuiVer\n\n        ndk {\n            abiFilters.add(\"arm64-v8a\")\n        }\n    }\n    buildTypes {\n        getByName(\"release\") {\n            isMinifyEnabled = true\n            proguardFiles(getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\")\n            signingConfig = signingConfigs.getByName(\"release\")\n        }\n    }\n}\n\ndependencies {\n    implementation(Dep.AndroidX.appcompat)\n    implementation(Dep.AndroidX.annotation)\n    implementation(Dep.AndroidX.activity)\n    implementation(Dep.MaterialDesign.material)\n    implementation(Dep.ButterKnife.butterknife)\n    implementation(Dep.Compose.activity)\n    implementation(Dep.Compose.constraintlayout)\n    kapt(Dep.ButterKnife.compiler)\n    implementation(project(\":lib\"))\n    implementation(project(\":qmui\"))\n    implementation(project(\":arch\"))\n    implementation(project(\":type\"))\n    implementation(project(\":compose\"))\n    implementation(project(\":photo\"))\n    implementation(project(\":photo-coil\"))\n    implementation(project(\":photo-glide\"))\n    implementation(project(\":editor\"))\n    implementation(Dep.Flipper.soLoader)\n    implementation(Dep.Flipper.flipper)\n    kapt(project(\":compiler\"))\n    kapt(project(\":arch-compiler\"))\n    kapt(Dep.Glide.compiler)\n\n    implementation(\"com.iqiyi.xcrash:xcrash-android-lib:3.1.0\")\n}\n"
  },
  {
    "path": "qmuidemo/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<lint>\n    <!-- Disable the given check in this project -->\n    <issue id=\"HardcodedText\" severity=\"ignore\"/>\n    <issue id=\"SmallSp\" severity=\"ignore\"/>\n    <issue id=\"IconMissingDensityFolder\" severity=\"ignore\"/>\n    <issue id=\"RtlHardcoded\" severity=\"ignore\"/>\n    <issue id=\"Deprecated\" severity=\"warning\">\n        <ignore regexp=\"singleLine\"/>\n    </issue>\n</lint>\n"
  },
  {
    "path": "qmuidemo/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/chant/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n-keep class **_FragmentFinder { *; }\n-keep class androidx.fragment.app.* { *; }\n\n-keep class com.qmuiteam.qmui.arch.record.RecordIdClassMap { *; }\n-keep class com.qmuiteam.qmui.arch.record.RecordIdClassMapImpl { *; }\n\n-keep class com.qmuiteam.qmui.arch.scheme.SchemeMap {*;}\n-keep class com.qmuiteam.qmui.arch.scheme.SchemeMapImpl {*;}\n\n-keep class com.facebook.jni.**{*;}\n-keep class com.facebook.flipper.**{*;}"
  },
  {
    "path": "qmuidemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    package=\"com.qmuiteam.qmuidemo\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n\n    <application\n        android:name=\".QDApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:theme=\"@style/AppTheme\"\n        tools:ignore=\"AllowBackup,GoogleAppIndexingWarning\"\n        tools:targetApi=\"n\">\n\n        <meta-data\n            android:name=\"android.notch_support\"\n            android:value=\"true\"/>\n\n        <meta-data\n            android:name=\"android.max_aspect\"\n            android:value=\"2.34\"/>\n\n        <meta-data\n            android:name=\"notch.config\"\n            android:value=\"portrait|landscape\"/>\n\n        <activity\n            android:name=\".QDMainActivity\"\n            android:label=\"@string/app_name\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode\"\n            android:windowSoftInputMode=\"stateAlwaysHidden|adjustResize\"\n            android:exported=\"false\"/>\n        <activity\n            android:name=\".activity.LauncherActivity\"\n            android:theme=\"@style/AppTheme.Launcher\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".activity.TranslucentActivity\"\n            android:label=\"@string/app_name\"\n            android:exported=\"false\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode\"\n            android:windowSoftInputMode=\"stateAlwaysHidden|adjustResize\">\n        </activity>\n\n        <activity\n            android:name=\".activity.ArchTestActivity\"\n            android:exported=\"false\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode\"\n            android:label=\"@string/app_name\"\n            android:windowSoftInputMode=\"stateAlwaysHidden|adjustResize\">\n        </activity>\n\n        <activity\n            android:name=\".activity.TestArchInViewPagerActivity\"\n            android:exported=\"false\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:label=\"@string/app_name\"\n            android:windowSoftInputMode=\"stateAlwaysHidden|adjustResize\">\n        </activity>\n\n        <activity\n            android:name=\"com.qmuiteam.photo.activity.QMUIPhotoViewerActivity\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\">\n\n        </activity>\n\n        <activity\n            android:name=\"com.qmuiteam.photo.activity.QMUIPhotoPickerActivity\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\">\n\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "qmuidemo/src/main/assets/demo.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover\">\n    <title>\n        js调用java\n    </title>\n</head>\n\n<body>\n\n<div>\n    <input type=\"button\" id=\"test1\" value=\"发消息给Native\" onclick=\"testClick();\"/>\n    <input type=\"button\" id=\"test2\" value=\"原生不支持的指令\" onclick=\"testClick1();\"/>\n\n    <div>\n        <a href=\"qmui://tab?mode=2&name=你好\">scheme 跳转到 Tab</a>\n    </div>\n\n</div>\n<div id=\"message_console\">\n\n</div>\n</body>\n<script>\n    var messageConsoleBox = document.getElementById(\"message_console\");\n\n    function testClick() {\n\n         window.QMUIBridge.isCmdSupport(\"test\", function(support){\n            if(support){\n                var data = {cmd: \"test\", id: 1, info: \"来自 Webview 的消息\"};\n                window.QMUIBridge.send(data, function (responseData) {\n                    var text = document.createElement('p');\n                    text.innerText = \"code = \" + responseData.code + \"; message = \" + responseData.message;\n                    messageConsoleBox.appendChild(text);\n                });\n            }else{\n                var text = document.createElement('p');\n                text.innerText = \"cmd (test) is not supported. \"\n                messageConsoleBox.appendChild(text);\n            }\n         })\n\n    }\n\n    function testClick1() {\n        window.QMUIBridge.isCmdSupport(\"test2\", function(support){\n            if(support){\n                var data = {cmd: \"test2\", id: 1, info: \"来自 Webview 的消息\"};\n                window.QMUIBridge.send(data, function (responseData) {\n                    var text = document.createElement('p');\n                    text.innerText = \"code = \" + responseData.code + \"; message = \" + responseData.message;\n                    messageConsoleBox.appendChild(text);\n                });\n            }else{\n                var text = document.createElement('p');\n                text.innerText = \"cmd (test2) is not supported. \"\n                messageConsoleBox.appendChild(text);\n            }\n         })\n    }\n\n    document.addEventListener('QMUIBridgeReady', function () {\n        var text = document.createElement('p')\n        text.innerText = \"Bridge加载完成。\"\n        messageConsoleBox.appendChild(text)\n\n        window.QMUIBridge.getSupportedCmdList(function(data){\n            var text = document.createElement('p')\n            var cmdList = \"原生支持的指令：\"\n            if(data && data.length){\n                for(var i = 0; i< data.length; i++){\n                    cmdList += data[i]\n                    if(i < data.length - 1){\n                        cmdList += \", \"\n                    }\n                }\n            }\n            text.innerText = cmdList\n            messageConsoleBox.appendChild(text)\n        })\n    }, false);\n</script>\n\n</html>\n\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/QDApplication.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo\n\nimport android.annotation.SuppressLint\nimport android.app.Application\nimport android.content.ContentValues\nimport android.content.Context\nimport android.content.res.Configuration\nimport android.os.Environment\nimport android.provider.MediaStore\nimport android.util.Log\nimport coil.ImageLoader\nimport coil.ImageLoaderFactory\nimport com.facebook.flipper.android.AndroidFlipperClient\nimport com.facebook.flipper.plugins.inspector.DescriptorMapping\nimport com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin\nimport com.facebook.soloader.SoLoader\nimport com.qmuiteam.qmui.QMUILog\nimport com.qmuiteam.qmui.QMUILog.QMUILogDelegate\nimport com.qmuiteam.qmui.arch.QMUISwipeBackActivityManager\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceCompiler\nimport com.qmuiteam.qmuidemo.manager.QDSkinManager\nimport com.qmuiteam.qmuidemo.manager.QDUpgradeManager\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport xcrash.TombstoneManager\nimport xcrash.XCrash\nimport java.io.File\n\n\n/**\n * Demo 的 Application 入口。\n * Created by cgine on 16/3/22.\n */\nclass QDApplication : Application(), ImageLoaderFactory {\n\n    override fun attachBaseContext(base: Context?) {\n        super.attachBaseContext(base)\n        XCrash.init(this)\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        context = applicationContext\n        QMUILog.setDelegete(object : QMUILogDelegate {\n            override fun e(tag: String, msg: String, vararg obj: Any) {\n                Log.e(tag, msg)\n            }\n\n            override fun w(tag: String, msg: String, vararg obj: Any) {\n                Log.w(tag, msg)\n            }\n\n            override fun i(tag: String, msg: String, vararg obj: Any) {\n                Log.i(tag, msg)\n            }\n\n            override fun d(tag: String, msg: String, vararg obj: Any) {\n                Log.d(tag, msg)\n            }\n\n            override fun printErrStackTrace(tag: String, tr: Throwable, format: String, vararg obj: Any) {}\n        })\n        QDUpgradeManager.getInstance(this).check()\n        QMUISwipeBackActivityManager.init(this)\n        QMUIQQFaceCompiler.setDefaultQQFaceManager(QDQQFaceManager.getInstance())\n        QDSkinManager.install(this)\n        if(BuildConfig.DEBUG){\n            SoLoader.init(this, false)\n            val client = AndroidFlipperClient.getInstance(this)\n            client.addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()))\n            client.start()\n        }\n\n        GlobalScope.launch(Dispatchers.IO) {\n            delay(5000)\n            for (file in TombstoneManager.getAllTombstones()) {\n                try {\n                    val contentValues = ContentValues()\n                    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name)\n                    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, \"txt\")\n                    val uri = contentResolver.insert(MediaStore.Files.getContentUri(\"external\"), contentValues) ?: continue\n                    contentResolver.openOutputStream(uri)?.use { out ->\n                        file.inputStream().use { ins ->\n                            ins.copyTo(out)\n                        }\n                    }\n                    file.delete()\n                }catch (ignore: Throwable){\n\n                }\n\n            }\n        }\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        if (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {\n            QDSkinManager.changeSkin(QDSkinManager.SKIN_DARK)\n        } else if (QDSkinManager.getCurrentSkin() == QDSkinManager.SKIN_DARK) {\n            QDSkinManager.changeSkin(QDSkinManager.SKIN_BLUE)\n        }\n    }\n\n    override fun newImageLoader(): ImageLoader {\n        return ImageLoader.Builder(applicationContext)\n            .crossfade(true)\n            .build()\n    }\n\n    companion object {\n        @JvmStatic\n        @SuppressLint(\"StaticFieldLeak\")\n        var context: Context? = null\n            private set\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/QDMainActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo;\n\nimport static com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment.EXTRA_TITLE;\nimport static com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment.EXTRA_URL;\n\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.os.Bundle;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentContainerView;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity;\nimport com.qmuiteam.qmui.arch.annotation.DefaultFirstFragment;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIStatusBarHelper;\nimport com.qmuiteam.qmui.util.QMUIViewOffsetHelper;\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView2;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.popup.QMUIPopup;\nimport com.qmuiteam.qmui.widget.popup.QMUIPopups;\nimport com.qmuiteam.qmuidemo.base.BaseFragmentActivity;\nimport com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment;\nimport com.qmuiteam.qmuidemo.fragment.home.HomeFragment;\nimport com.qmuiteam.qmuidemo.manager.QDSkinManager;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n@DefaultFirstFragment(HomeFragment.class)\n@LatestVisitRecord\npublic class QDMainActivity extends BaseFragmentActivity {\n\n    private QMUIPopup mGlobalAction;\n\n    private QMUISkinManager.OnSkinChangeListener mOnSkinChangeListener = new QMUISkinManager.OnSkinChangeListener() {\n        @Override\n        public void onSkinChange(QMUISkinManager skinManager, int oldSkin, int newSkin) {\n            if (newSkin == QDSkinManager.SKIN_WHITE) {\n                QMUIStatusBarHelper.setStatusBarLightMode(QDMainActivity.this);\n            } else {\n                QMUIStatusBarHelper.setStatusBarDarkMode(QDMainActivity.this);\n            }\n        }\n    };\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        QMUISkinManager skinManager = QMUISkinManager.defaultInstance(this);\n        setSkinManager(skinManager);\n        mOnSkinChangeListener.onSkinChange(skinManager, -1, skinManager.getCurrentSkin());\n    }\n\n    @Override\n    protected RootView onCreateRootView(int fragmentContainerId) {\n        return new CustomRootView(this, fragmentContainerId);\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n    }\n\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        if (getSkinManager() != null) {\n            getSkinManager().addSkinChangeListener(mOnSkinChangeListener);\n        }\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        if (getSkinManager() != null) {\n            getSkinManager().removeSkinChangeListener(mOnSkinChangeListener);\n        }\n    }\n\n    private void showGlobalActionPopup(View v) {\n        String[] listItems = new String[]{\n                \"Change Skin\"\n        };\n        List<String> data = new ArrayList<>();\n\n        Collections.addAll(data, listItems);\n\n        ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.simple_list_item, data);\n        AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {\n                if (i == 0) {\n                    final String[] items = new String[]{\"蓝色（默认）\", \"黑色\", \"白色\"};\n                    new QMUIDialog.MenuDialogBuilder(QDMainActivity.this)\n                            .addItems(items, new DialogInterface.OnClickListener() {\n                                @Override\n                                public void onClick(DialogInterface dialog, int which) {\n                                    QDSkinManager.changeSkin(which + 1);\n                                    dialog.dismiss();\n                                }\n                            })\n                            .setSkinManager(QMUISkinManager.defaultInstance(QDMainActivity.this))\n                            .create()\n                            .show();\n                }\n                if (mGlobalAction != null) {\n                    mGlobalAction.dismiss();\n                }\n            }\n        };\n        mGlobalAction = QMUIPopups.listPopup(this,\n                QMUIDisplayHelper.dp2px(this, 250),\n                QMUIDisplayHelper.dp2px(this, 300),\n                adapter,\n                onItemClickListener)\n                .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)\n                .preferredDirection(QMUIPopup.DIRECTION_TOP)\n                .shadow(true)\n                .edgeProtection(QMUIDisplayHelper.dp2px(this, 10))\n                .offsetYIfTop(QMUIDisplayHelper.dp2px(this, 5))\n                .skinManager(QMUISkinManager.defaultInstance(this))\n                .show(v);\n    }\n\n\n    public static Intent createWebExplorerIntent(Context context, String url, String title) {\n        Bundle bundle = new Bundle();\n        bundle.putString(EXTRA_URL, url);\n        bundle.putString(EXTRA_TITLE, title);\n        return of(context, QDWebExplorerFragment.class, bundle);\n    }\n\n    public static Intent of(@NonNull Context context,\n                            @NonNull Class<? extends QMUIFragment> firstFragment) {\n        return QMUIFragmentActivity.intentOf(context, QDMainActivity.class, firstFragment);\n    }\n\n    public static Intent of(@NonNull Context context,\n                            @NonNull Class<? extends QMUIFragment> firstFragment,\n                            @Nullable Bundle fragmentArgs) {\n        return QMUIFragmentActivity.intentOf(context, QDMainActivity.class, firstFragment, fragmentArgs);\n    }\n\n    class CustomRootView extends RootView {\n\n        private FragmentContainerView fragmentContainer;\n        private QMUIRadiusImageView2 globalBtn;\n        private QMUIViewOffsetHelper globalBtnOffsetHelper;\n        private int btnSize;\n        private final int touchSlop;\n        private float touchDownX = 0;\n        private float touchDownY = 0;\n        private float lastTouchX = 0;\n        private float lastTouchY = 0;\n        private boolean isDragging;\n        private boolean isTouchDownInGlobalBtn = false;\n\n        public CustomRootView(Context context, int fragmentContainerId) {\n            super(context, fragmentContainerId);\n\n            btnSize = QMUIDisplayHelper.dp2px(context, 56);\n\n            fragmentContainer = new FragmentContainerView(context);\n            fragmentContainer.setId(fragmentContainerId);\n            addView(fragmentContainer, new FrameLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n\n\n            globalBtn = new QMUIRadiusImageView2(context);\n            globalBtn.setImageResource(R.mipmap.icon_theme);\n            globalBtn.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n            globalBtn.setRadiusAndShadow(btnSize / 2,\n                    QMUIDisplayHelper.dp2px(getContext(), 16), 0.4f);\n            globalBtn.setBorderWidth(1);\n            globalBtn.setBorderColor(QMUIResHelper.getAttrColor(context, R.attr.qmui_skin_support_color_separator));\n            globalBtn.setBackgroundColor(QMUIResHelper.getAttrColor(context, R.attr.app_skin_common_background));\n            globalBtn.setOnClickListener(new OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    showGlobalActionPopup(v);\n                }\n            });\n            FrameLayout.LayoutParams globalBtnLp = new FrameLayout.LayoutParams(btnSize, btnSize);\n            globalBtnLp.gravity = Gravity.BOTTOM | Gravity.RIGHT;\n            globalBtnLp.bottomMargin = QMUIDisplayHelper.dp2px(context, 60);\n            globalBtnLp.rightMargin = QMUIDisplayHelper.dp2px(context, 24);\n            QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n            builder.background(R.attr.app_skin_common_background);\n            builder.border(R.attr.qmui_skin_support_color_separator);\n            builder.tintColor(R.attr.app_skin_common_img_tint_color);\n            QMUISkinHelper.setSkinValue(globalBtn, builder);\n            builder.release();\n            addView(globalBtn, globalBtnLp);\n            globalBtnOffsetHelper = new QMUIViewOffsetHelper(globalBtn);\n            touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();\n        }\n\n        @Override\n        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n            super.onLayout(changed, left, top, right, bottom);\n            globalBtnOffsetHelper.onViewLayout();\n        }\n\n        @Override\n        public boolean onInterceptTouchEvent(MotionEvent event) {\n            float x = event.getX(), y = event.getY();\n            int action = event.getAction();\n            if (action == MotionEvent.ACTION_DOWN) {\n                isTouchDownInGlobalBtn = isDownInGlobalBtn(x, y);\n                touchDownX = lastTouchX = x;\n                touchDownY = lastTouchY = y;\n            } else if (action == MotionEvent.ACTION_MOVE) {\n                if (!isDragging && isTouchDownInGlobalBtn) {\n                    int dx = (int) (x - touchDownX);\n                    int dy = (int) (y - touchDownY);\n                    if (Math.sqrt(dx * dx + dy * dy) > touchSlop) {\n                        isDragging = true;\n                    }\n                }\n\n                if (isDragging) {\n                    int dx = (int) (x - lastTouchX);\n                    int dy = (int) (y - lastTouchY);\n                    int gx = globalBtn.getLeft();\n                    int gy = globalBtn.getTop();\n                    int gw = globalBtn.getWidth(), w = getWidth();\n                    int gh = globalBtn.getHeight(), h = getHeight();\n                    if (gx + dx < 0) {\n                        dx = -gx;\n                    } else if (gx + dx + gw > w) {\n                        dx = w - gw - gx;\n                    }\n\n                    if (gy + dy < 0) {\n                        dy = -gy;\n                    } else if (gy + dy + gh > h) {\n                        dy = h - gh - gy;\n                    }\n                    globalBtnOffsetHelper.setLeftAndRightOffset(\n                            globalBtnOffsetHelper.getLeftAndRightOffset() + dx);\n                    globalBtnOffsetHelper.setTopAndBottomOffset(\n                            globalBtnOffsetHelper.getTopAndBottomOffset() + dy);\n                }\n                lastTouchX = x;\n                lastTouchY = y;\n            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\n                isDragging = false;\n                isTouchDownInGlobalBtn = false;\n            }\n            return isDragging;\n        }\n\n        private boolean isDownInGlobalBtn(float x, float y) {\n            return globalBtn.getLeft() < x && globalBtn.getRight() > x &&\n                    globalBtn.getTop() < y && globalBtn.getBottom() > y;\n        }\n\n        @Override\n        public boolean onTouchEvent(MotionEvent event) {\n            float x = event.getX(), y = event.getY();\n            int action = event.getAction();\n            if (action == MotionEvent.ACTION_DOWN) {\n                isTouchDownInGlobalBtn = isDownInGlobalBtn(x, y);\n                touchDownX = lastTouchX = x;\n                touchDownY = lastTouchY = y;\n            } else if (action == MotionEvent.ACTION_MOVE) {\n                if (!isDragging && isTouchDownInGlobalBtn) {\n                    int dx = (int) (x - touchDownX);\n                    int dy = (int) (y - touchDownY);\n                    if (Math.sqrt(dx * dx + dy * dy) > touchSlop) {\n                        isDragging = true;\n                    }\n                }\n\n                if (isDragging) {\n                    int dx = (int) (x - lastTouchX);\n                    int dy = (int) (y - lastTouchY);\n                    int gx = globalBtn.getLeft();\n                    int gy = globalBtn.getTop();\n                    int gw = globalBtn.getWidth(), w = getWidth();\n                    int gh = globalBtn.getHeight(), h = getHeight();\n                    if (gx + dx < 0) {\n                        dx = -gx;\n                    } else if (gx + dx + gw > w) {\n                        dx = w - gw - gx;\n                    }\n\n                    if (gy + dy < 0) {\n                        dy = -gy;\n                    } else if (gy + dy + gh > h) {\n                        dy = h - gh - gy;\n                    }\n                    globalBtnOffsetHelper.setLeftAndRightOffset(\n                            globalBtnOffsetHelper.getLeftAndRightOffset() + dx);\n                    globalBtnOffsetHelper.setTopAndBottomOffset(\n                            globalBtnOffsetHelper.getTopAndBottomOffset() + dy);\n                }\n                lastTouchX = x;\n                lastTouchY = y;\n            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\n                isDragging = false;\n                isTouchDownInGlobalBtn = false;\n            }\n            return isDragging || super.onTouchEvent(event);\n        }\n\n        @Override\n        public FragmentContainerView getFragmentContainerView() {\n            return fragmentContainer;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/QDQQFaceManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo;\n\nimport android.graphics.drawable.Drawable;\nimport androidx.collection.ArrayMap;\nimport androidx.core.content.ContextCompat;\n\nimport android.util.Log;\nimport android.util.SparseIntArray;\n\nimport com.qmuiteam.qmui.qqface.IQMUIQQFaceManager;\nimport com.qmuiteam.qmui.qqface.QQFace;\nimport com.qmuiteam.qmui.type.parser.EmojiResourceProvider;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2016-12-21\n */\n\npublic class QDQQFaceManager implements IQMUIQQFaceManager, EmojiResourceProvider {\n    private static final HashMap<String, Integer> sQQFaceMap = new HashMap<>();\n    private static final List<QQFace> mQQFaceList = new ArrayList<>();\n    private static final SparseIntArray sEmojisMap = new SparseIntArray(846);\n    private static final SparseIntArray sSoftbanksMap = new SparseIntArray(471);\n    @SuppressWarnings(\"MismatchedQueryAndUpdateOfCollection\") private static final ArrayMap<String, String> mQQFaceFileNameList = new ArrayMap<>();//存储QQ表情对应的文件名,方便混淆后可以获取到原文件名\n\n    private static QDQQFaceManager sQDQQFaceManager = new QDQQFaceManager();\n\n    static {\n        long start = System.currentTimeMillis();\n\n        mQQFaceList.add(new QQFace(\"[微笑]\", R.drawable.smiley_0));\n        mQQFaceList.add(new QQFace(\"[撇嘴]\", R.drawable.smiley_1));\n        mQQFaceList.add(new QQFace(\"[色]\", R.drawable.smiley_2));\n        mQQFaceList.add(new QQFace(\"[发呆]\", R.drawable.smiley_3));\n        mQQFaceList.add(new QQFace(\"[得意]\", R.drawable.smiley_4));\n        mQQFaceList.add(new QQFace(\"[流泪]\", R.drawable.smiley_5));\n        mQQFaceList.add(new QQFace(\"[害羞]\", R.drawable.smiley_6));\n        mQQFaceList.add(new QQFace(\"[闭嘴]\", R.drawable.smiley_7));\n        mQQFaceList.add(new QQFace(\"[睡]\", R.drawable.smiley_8));\n        mQQFaceList.add(new QQFace(\"[大哭]\", R.drawable.smiley_9));\n        mQQFaceList.add(new QQFace(\"[尴尬]\", R.drawable.smiley_10));\n        mQQFaceList.add(new QQFace(\"[发怒]\", R.drawable.smiley_11));\n        mQQFaceList.add(new QQFace(\"[调皮]\", R.drawable.smiley_12));\n        mQQFaceList.add(new QQFace(\"[呲牙]\", R.drawable.smiley_13));\n        mQQFaceList.add(new QQFace(\"[惊讶]\", R.drawable.smiley_14));\n        mQQFaceList.add(new QQFace(\"[难过]\", R.drawable.smiley_15));\n        mQQFaceList.add(new QQFace(\"[酷]\", R.drawable.smiley_16));\n        mQQFaceList.add(new QQFace(\"[冷汗]\", R.drawable.smiley_17));\n        mQQFaceList.add(new QQFace(\"[抓狂]\", R.drawable.smiley_18));\n        mQQFaceList.add(new QQFace(\"[吐]\", R.drawable.smiley_19));\n        mQQFaceList.add(new QQFace(\"[偷笑]\", R.drawable.smiley_20));\n        mQQFaceList.add(new QQFace(\"[可爱]\", R.drawable.smiley_21));\n        mQQFaceList.add(new QQFace(\"[白眼]\", R.drawable.smiley_22));\n        mQQFaceList.add(new QQFace(\"[傲慢]\", R.drawable.smiley_23));\n        mQQFaceList.add(new QQFace(\"[饥饿]\", R.drawable.smiley_24));\n        mQQFaceList.add(new QQFace(\"[困]\", R.drawable.smiley_25));\n        mQQFaceList.add(new QQFace(\"[惊恐]\", R.drawable.smiley_26));\n        mQQFaceList.add(new QQFace(\"[流汗]\", R.drawable.smiley_27));\n        mQQFaceList.add(new QQFace(\"[憨笑]\", R.drawable.smiley_28));\n        mQQFaceList.add(new QQFace(\"[大兵]\", R.drawable.smiley_29));\n        mQQFaceList.add(new QQFace(\"[奋斗]\", R.drawable.smiley_30));\n        mQQFaceList.add(new QQFace(\"[咒骂]\", R.drawable.smiley_31));\n        mQQFaceList.add(new QQFace(\"[疑问]\", R.drawable.smiley_32));\n        mQQFaceList.add(new QQFace(\"[嘘]\", R.drawable.smiley_33));\n        mQQFaceList.add(new QQFace(\"[晕]\", R.drawable.smiley_34));\n        mQQFaceList.add(new QQFace(\"[折磨]\", R.drawable.smiley_35));\n        mQQFaceList.add(new QQFace(\"[衰]\", R.drawable.smiley_36));\n        mQQFaceList.add(new QQFace(\"[骷髅]\", R.drawable.smiley_37));\n        mQQFaceList.add(new QQFace(\"[敲打]\", R.drawable.smiley_38));\n        mQQFaceList.add(new QQFace(\"[再见]\", R.drawable.smiley_39));\n        mQQFaceList.add(new QQFace(\"[擦汗]\", R.drawable.smiley_40));\n        mQQFaceList.add(new QQFace(\"[抠鼻]\", R.drawable.smiley_41));\n        mQQFaceList.add(new QQFace(\"[鼓掌]\", R.drawable.smiley_42));\n        mQQFaceList.add(new QQFace(\"[糗大了]\", R.drawable.smiley_43));\n        mQQFaceList.add(new QQFace(\"[坏笑]\", R.drawable.smiley_44));\n        mQQFaceList.add(new QQFace(\"[左哼哼]\", R.drawable.smiley_45));\n        mQQFaceList.add(new QQFace(\"[右哼哼]\", R.drawable.smiley_46));\n        mQQFaceList.add(new QQFace(\"[哈欠]\", R.drawable.smiley_47));\n        mQQFaceList.add(new QQFace(\"[鄙视]\", R.drawable.smiley_48));\n        mQQFaceList.add(new QQFace(\"[委屈]\", R.drawable.smiley_49));\n        mQQFaceList.add(new QQFace(\"[快哭了]\", R.drawable.smiley_50));\n        mQQFaceList.add(new QQFace(\"[阴险]\", R.drawable.smiley_51));\n        mQQFaceList.add(new QQFace(\"[亲亲]\", R.drawable.smiley_52));\n        mQQFaceList.add(new QQFace(\"[吓]\", R.drawable.smiley_53));\n        mQQFaceList.add(new QQFace(\"[可怜]\", R.drawable.smiley_54));\n        mQQFaceList.add(new QQFace(\"[菜刀]\", R.drawable.smiley_55));\n        mQQFaceList.add(new QQFace(\"[西瓜]\", R.drawable.smiley_56));\n        mQQFaceList.add(new QQFace(\"[啤酒]\", R.drawable.smiley_57));\n        mQQFaceList.add(new QQFace(\"[篮球]\", R.drawable.smiley_58));\n        mQQFaceList.add(new QQFace(\"[乒乓]\", R.drawable.smiley_59));\n        mQQFaceList.add(new QQFace(\"[咖啡]\", R.drawable.smiley_60));\n        mQQFaceList.add(new QQFace(\"[饭]\", R.drawable.smiley_61));\n        mQQFaceList.add(new QQFace(\"[猪头]\", R.drawable.smiley_62));\n        mQQFaceList.add(new QQFace(\"[玫瑰]\", R.drawable.smiley_63));\n        mQQFaceList.add(new QQFace(\"[凋谢]\", R.drawable.smiley_64));\n        mQQFaceList.add(new QQFace(\"[示爱]\", R.drawable.smiley_65));\n        mQQFaceList.add(new QQFace(\"[爱心]\", R.drawable.smiley_66));\n        mQQFaceList.add(new QQFace(\"[心碎]\", R.drawable.smiley_67));\n        mQQFaceList.add(new QQFace(\"[蛋糕]\", R.drawable.smiley_68));\n        mQQFaceList.add(new QQFace(\"[闪电]\", R.drawable.smiley_69));\n        mQQFaceList.add(new QQFace(\"[炸弹]\", R.drawable.smiley_70));\n        mQQFaceList.add(new QQFace(\"[刀]\", R.drawable.smiley_71));\n        mQQFaceList.add(new QQFace(\"[足球]\", R.drawable.smiley_72));\n        mQQFaceList.add(new QQFace(\"[瓢虫]\", R.drawable.smiley_73));\n        mQQFaceList.add(new QQFace(\"[便便]\", R.drawable.smiley_74));\n        mQQFaceList.add(new QQFace(\"[月亮]\", R.drawable.smiley_75));\n        mQQFaceList.add(new QQFace(\"[太阳]\", R.drawable.smiley_76));\n        mQQFaceList.add(new QQFace(\"[礼物]\", R.drawable.smiley_77));\n        mQQFaceList.add(new QQFace(\"[拥抱]\", R.drawable.smiley_78));\n        mQQFaceList.add(new QQFace(\"[强]\", R.drawable.smiley_79));\n        mQQFaceList.add(new QQFace(\"[弱]\", R.drawable.smiley_80));\n        mQQFaceList.add(new QQFace(\"[握手]\", R.drawable.smiley_81));\n        mQQFaceList.add(new QQFace(\"[胜利]\", R.drawable.smiley_82));\n        mQQFaceList.add(new QQFace(\"[抱拳]\", R.drawable.smiley_83));\n        mQQFaceList.add(new QQFace(\"[勾引]\", R.drawable.smiley_84));\n        mQQFaceList.add(new QQFace(\"[拳头]\", R.drawable.smiley_85));\n        mQQFaceList.add(new QQFace(\"[差劲]\", R.drawable.smiley_86));\n        mQQFaceList.add(new QQFace(\"[爱你]\", R.drawable.smiley_87));\n        mQQFaceList.add(new QQFace(\"[NO]\", R.drawable.smiley_88));\n        mQQFaceList.add(new QQFace(\"[OK]\", R.drawable.smiley_89));\n        mQQFaceList.add(new QQFace(\"[爱情]\", R.drawable.smiley_90));\n        mQQFaceList.add(new QQFace(\"[飞吻]\", R.drawable.smiley_91));\n        mQQFaceList.add(new QQFace(\"[跳跳]\", R.drawable.smiley_92));\n        mQQFaceList.add(new QQFace(\"[发抖]\", R.drawable.smiley_93));\n        mQQFaceList.add(new QQFace(\"[怄火]\", R.drawable.smiley_94));\n        mQQFaceList.add(new QQFace(\"[转圈]\", R.drawable.smiley_95));\n        mQQFaceList.add(new QQFace(\"[磕头]\", R.drawable.smiley_96));\n        mQQFaceList.add(new QQFace(\"[回头]\", R.drawable.smiley_97));\n        mQQFaceList.add(new QQFace(\"[跳绳]\", R.drawable.smiley_98));\n        mQQFaceList.add(new QQFace(\"[挥手]\", R.drawable.smiley_99));\n        mQQFaceList.add(new QQFace(\"[激动]\", R.drawable.smiley_100));\n        mQQFaceList.add(new QQFace(\"[街舞]\", R.drawable.smiley_101));\n        mQQFaceList.add(new QQFace(\"[献吻]\", R.drawable.smiley_102));\n        mQQFaceList.add(new QQFace(\"[左太极]\", R.drawable.smiley_103));\n        mQQFaceList.add(new QQFace(\"[右太极]\", R.drawable.smiley_104));\n\n        for (QQFace face : mQQFaceList) {\n            sQQFaceMap.put(face.getName(), face.getRes());\n        }\n\n        mQQFaceFileNameList.put(\"[微笑]\", \"smiley_0\");\n        mQQFaceFileNameList.put(\"[撇嘴]\", \"smiley_1\");\n        mQQFaceFileNameList.put(\"[色]\", \"smiley_2\");\n        mQQFaceFileNameList.put(\"[发呆]\", \"smiley_3\");\n        mQQFaceFileNameList.put(\"[得意]\", \"smiley_4\");\n        mQQFaceFileNameList.put(\"[流泪]\", \"smiley_5\");\n        mQQFaceFileNameList.put(\"[害羞]\", \"smiley_6\");\n        mQQFaceFileNameList.put(\"[闭嘴]\", \"smiley_7\");\n        mQQFaceFileNameList.put(\"[睡]\", \"smiley_8\");\n        mQQFaceFileNameList.put(\"[大哭]\", \"smiley_9\");\n        mQQFaceFileNameList.put(\"[尴尬]\", \"smiley_10\");\n        mQQFaceFileNameList.put(\"[发怒]\", \"smiley_11\");\n        mQQFaceFileNameList.put(\"[调皮]\", \"smiley_12\");\n        mQQFaceFileNameList.put(\"[呲牙]\", \"smiley_13\");\n        mQQFaceFileNameList.put(\"[惊讶]\", \"smiley_14\");\n        mQQFaceFileNameList.put(\"[难过]\", \"smiley_15\");\n        mQQFaceFileNameList.put(\"[酷]\", \"smiley_16\");\n        mQQFaceFileNameList.put(\"[冷汗]\", \"smiley_17\");\n        mQQFaceFileNameList.put(\"[抓狂]\", \"smiley_18\");\n        mQQFaceFileNameList.put(\"[吐]\", \"smiley_19\");\n        mQQFaceFileNameList.put(\"[偷笑]\", \"smiley_20\");\n        mQQFaceFileNameList.put(\"[可爱]\", \"smiley_21\");\n        mQQFaceFileNameList.put(\"[白眼]\", \"smiley_22\");\n        mQQFaceFileNameList.put(\"[傲慢]\", \"smiley_23\");\n        mQQFaceFileNameList.put(\"[饥饿]\", \"smiley_24\");\n        mQQFaceFileNameList.put(\"[困]\", \"smiley_25\");\n        mQQFaceFileNameList.put(\"[惊恐]\", \"smiley_26\");\n        mQQFaceFileNameList.put(\"[流汗]\", \"smiley_27\");\n        mQQFaceFileNameList.put(\"[憨笑]\", \"smiley_28\");\n        mQQFaceFileNameList.put(\"[大兵]\", \"smiley_29\");\n        mQQFaceFileNameList.put(\"[奋斗]\", \"smiley_30\");\n        mQQFaceFileNameList.put(\"[咒骂]\", \"smiley_31\");\n        mQQFaceFileNameList.put(\"[疑问]\", \"smiley_32\");\n        mQQFaceFileNameList.put(\"[嘘]\", \"smiley_33\");\n        mQQFaceFileNameList.put(\"[晕]\", \"smiley_34\");\n        mQQFaceFileNameList.put(\"[折磨]\", \"smiley_35\");\n        mQQFaceFileNameList.put(\"[衰]\", \"smiley_36\");\n        mQQFaceFileNameList.put(\"[骷髅]\", \"smiley_37\");\n        mQQFaceFileNameList.put(\"[敲打]\", \"smiley_38\");\n        mQQFaceFileNameList.put(\"[再见]\", \"smiley_39\");\n        mQQFaceFileNameList.put(\"[擦汗]\", \"smiley_40\");\n        mQQFaceFileNameList.put(\"[抠鼻]\", \"smiley_41\");\n        mQQFaceFileNameList.put(\"[鼓掌]\", \"smiley_42\");\n        mQQFaceFileNameList.put(\"[糗大了]\", \"smiley_43\");\n        mQQFaceFileNameList.put(\"[坏笑]\", \"smiley_44\");\n        mQQFaceFileNameList.put(\"[左哼哼]\", \"smiley_45\");\n        mQQFaceFileNameList.put(\"[右哼哼]\", \"smiley_46\");\n        mQQFaceFileNameList.put(\"[哈欠]\", \"smiley_47\");\n        mQQFaceFileNameList.put(\"[鄙视]\", \"smiley_48\");\n        mQQFaceFileNameList.put(\"[委屈]\", \"smiley_49\");\n        mQQFaceFileNameList.put(\"[快哭了]\", \"smiley_50\");\n        mQQFaceFileNameList.put(\"[阴险]\", \"smiley_51\");\n        mQQFaceFileNameList.put(\"[亲亲]\", \"smiley_52\");\n        mQQFaceFileNameList.put(\"[吓]\", \"smiley_53\");\n        mQQFaceFileNameList.put(\"[可怜]\", \"smiley_54\");\n        mQQFaceFileNameList.put(\"[菜刀]\", \"smiley_55\");\n        mQQFaceFileNameList.put(\"[西瓜]\", \"smiley_56\");\n        mQQFaceFileNameList.put(\"[啤酒]\", \"smiley_57\");\n        mQQFaceFileNameList.put(\"[篮球]\", \"smiley_58\");\n        mQQFaceFileNameList.put(\"[乒乓]\", \"smiley_59\");\n        mQQFaceFileNameList.put(\"[咖啡]\", \"smiley_60\");\n        mQQFaceFileNameList.put(\"[饭]\", \"smiley_61\");\n        mQQFaceFileNameList.put(\"[猪头]\", \"smiley_62\");\n        mQQFaceFileNameList.put(\"[玫瑰]\", \"smiley_63\");\n        mQQFaceFileNameList.put(\"[凋谢]\", \"smiley_64\");\n        mQQFaceFileNameList.put(\"[示爱]\", \"smiley_65\");\n        mQQFaceFileNameList.put(\"[爱心]\", \"smiley_66\");\n        mQQFaceFileNameList.put(\"[心碎]\", \"smiley_67\");\n        mQQFaceFileNameList.put(\"[蛋糕]\", \"smiley_68\");\n        mQQFaceFileNameList.put(\"[闪电]\", \"smiley_69\");\n        mQQFaceFileNameList.put(\"[炸弹]\", \"smiley_70\");\n        mQQFaceFileNameList.put(\"[刀]\", \"smiley_71\");\n        mQQFaceFileNameList.put(\"[足球]\", \"smiley_72\");\n        mQQFaceFileNameList.put(\"[瓢虫]\", \"smiley_73\");\n        mQQFaceFileNameList.put(\"[便便]\", \"smiley_74\");\n        mQQFaceFileNameList.put(\"[月亮]\", \"smiley_75\");\n        mQQFaceFileNameList.put(\"[太阳]\", \"smiley_76\");\n        mQQFaceFileNameList.put(\"[礼物]\", \"smiley_77\");\n        mQQFaceFileNameList.put(\"[拥抱]\", \"smiley_78\");\n        mQQFaceFileNameList.put(\"[强]\", \"smiley_79\");\n        mQQFaceFileNameList.put(\"[弱]\", \"smiley_80\");\n        mQQFaceFileNameList.put(\"[握手]\", \"smiley_81\");\n        mQQFaceFileNameList.put(\"[胜利]\", \"smiley_82\");\n        mQQFaceFileNameList.put(\"[抱拳]\", \"smiley_83\");\n        mQQFaceFileNameList.put(\"[勾引]\", \"smiley_84\");\n        mQQFaceFileNameList.put(\"[拳头]\", \"smiley_85\");\n        mQQFaceFileNameList.put(\"[差劲]\", \"smiley_86\");\n        mQQFaceFileNameList.put(\"[爱你]\", \"smiley_87\");\n        mQQFaceFileNameList.put(\"[NO]\", \"smiley_88\");\n        mQQFaceFileNameList.put(\"[OK]\", \"smiley_89\");\n        mQQFaceFileNameList.put(\"[爱情]\", \"smiley_90\");\n        mQQFaceFileNameList.put(\"[飞吻]\", \"smiley_91\");\n        mQQFaceFileNameList.put(\"[跳跳]\", \"smiley_92\");\n        mQQFaceFileNameList.put(\"[发抖]\", \"smiley_93\");\n        mQQFaceFileNameList.put(\"[怄火]\", \"smiley_94\");\n        mQQFaceFileNameList.put(\"[转圈]\", \"smiley_95\");\n        mQQFaceFileNameList.put(\"[磕头]\", \"smiley_96\");\n        mQQFaceFileNameList.put(\"[回头]\", \"smiley_97\");\n        mQQFaceFileNameList.put(\"[跳绳]\", \"smiley_98\");\n        mQQFaceFileNameList.put(\"[挥手]\", \"smiley_99\");\n        mQQFaceFileNameList.put(\"[激动]\", \"smiley_100\");\n        mQQFaceFileNameList.put(\"[街舞]\", \"smiley_101\");\n        mQQFaceFileNameList.put(\"[献吻]\", \"smiley_102\");\n        mQQFaceFileNameList.put(\"[左太极]\", \"smiley_103\");\n        mQQFaceFileNameList.put(\"[右太极]\", \"smiley_104\");\n\n        sEmojisMap.append(0x00a9, R.drawable.emoji_00a9);\n        sEmojisMap.append(0x00ae, R.drawable.emoji_00ae);\n        sEmojisMap.append(0x203c, R.drawable.emoji_203c);\n        sEmojisMap.append(0x2049, R.drawable.emoji_2049);\n        sEmojisMap.append(0x2122, R.drawable.emoji_2122);\n        sEmojisMap.append(0x2139, R.drawable.emoji_2139);\n        sEmojisMap.append(0x2194, R.drawable.emoji_2194);\n        sEmojisMap.append(0x2195, R.drawable.emoji_2195);\n        sEmojisMap.append(0x2196, R.drawable.emoji_2196);\n        sEmojisMap.append(0x2197, R.drawable.emoji_2197);\n        sEmojisMap.append(0x2198, R.drawable.emoji_2198);\n        sEmojisMap.append(0x2199, R.drawable.emoji_2199);\n        sEmojisMap.append(0x21a9, R.drawable.emoji_21a9);\n        sEmojisMap.append(0x21aa, R.drawable.emoji_21aa);\n        sEmojisMap.append(0x231a, R.drawable.emoji_231a);\n        sEmojisMap.append(0x231b, R.drawable.emoji_231b);\n        sEmojisMap.append(0x23e9, R.drawable.emoji_23e9);\n        sEmojisMap.append(0x23ea, R.drawable.emoji_23ea);\n        sEmojisMap.append(0x23eb, R.drawable.emoji_23eb);\n        sEmojisMap.append(0x23ec, R.drawable.emoji_23ec);\n        sEmojisMap.append(0x23f0, R.drawable.emoji_23f0);\n        sEmojisMap.append(0x23f3, R.drawable.emoji_23f3);\n        sEmojisMap.append(0x24c2, R.drawable.emoji_24c2);\n        sEmojisMap.append(0x25aa, R.drawable.emoji_25aa);\n        sEmojisMap.append(0x25ab, R.drawable.emoji_25ab);\n        sEmojisMap.append(0x25b6, R.drawable.emoji_25b6);\n        sEmojisMap.append(0x25c0, R.drawable.emoji_25c0);\n        sEmojisMap.append(0x25fb, R.drawable.emoji_25fb);\n        sEmojisMap.append(0x25fc, R.drawable.emoji_25fc);\n        sEmojisMap.append(0x25fd, R.drawable.emoji_25fd);\n        sEmojisMap.append(0x25fe, R.drawable.emoji_25fe);\n        sEmojisMap.append(0x2600, R.drawable.emoji_2600);\n        sEmojisMap.append(0x2601, R.drawable.emoji_2601);\n        sEmojisMap.append(0x260e, R.drawable.emoji_260e);\n        sEmojisMap.append(0x2611, R.drawable.emoji_2611);\n        sEmojisMap.append(0x2614, R.drawable.emoji_2614);\n        sEmojisMap.append(0x2615, R.drawable.emoji_2615);\n        sEmojisMap.append(0x261d, R.drawable.emoji_261d);\n        sEmojisMap.append(0x263a, R.drawable.emoji_263a);\n        sEmojisMap.append(0x2648, R.drawable.emoji_2648);\n        sEmojisMap.append(0x2649, R.drawable.emoji_2649);\n        sEmojisMap.append(0x264a, R.drawable.emoji_264a);\n        sEmojisMap.append(0x264b, R.drawable.emoji_264b);\n        sEmojisMap.append(0x264c, R.drawable.emoji_264c);\n        sEmojisMap.append(0x264d, R.drawable.emoji_264d);\n        sEmojisMap.append(0x264e, R.drawable.emoji_264e);\n        sEmojisMap.append(0x264f, R.drawable.emoji_264f);\n        sEmojisMap.append(0x2650, R.drawable.emoji_2650);\n        sEmojisMap.append(0x2651, R.drawable.emoji_2651);\n        sEmojisMap.append(0x2652, R.drawable.emoji_2652);\n        sEmojisMap.append(0x2653, R.drawable.emoji_2653);\n        sEmojisMap.append(0x2660, R.drawable.emoji_2660);\n        sEmojisMap.append(0x2663, R.drawable.emoji_2663);\n        sEmojisMap.append(0x2665, R.drawable.emoji_2665);\n        sEmojisMap.append(0x2666, R.drawable.emoji_2666);\n        sEmojisMap.append(0x2668, R.drawable.emoji_2668);\n        sEmojisMap.append(0x267b, R.drawable.emoji_267b);\n        sEmojisMap.append(0x267f, R.drawable.emoji_267f);\n        sEmojisMap.append(0x2693, R.drawable.emoji_2693);\n        sEmojisMap.append(0x26a0, R.drawable.emoji_26a0);\n        sEmojisMap.append(0x26a1, R.drawable.emoji_26a1);\n        sEmojisMap.append(0x26aa, R.drawable.emoji_26aa);\n        sEmojisMap.append(0x26ab, R.drawable.emoji_26ab);\n        sEmojisMap.append(0x26bd, R.drawable.emoji_26bd);\n        sEmojisMap.append(0x26be, R.drawable.emoji_26be);\n        sEmojisMap.append(0x26c4, R.drawable.emoji_26c4);\n        sEmojisMap.append(0x26c5, R.drawable.emoji_26c5);\n        sEmojisMap.append(0x26ce, R.drawable.emoji_26ce);\n        sEmojisMap.append(0x26d4, R.drawable.emoji_26d4);\n        sEmojisMap.append(0x26ea, R.drawable.emoji_26ea);\n        sEmojisMap.append(0x26f2, R.drawable.emoji_26f2);\n        sEmojisMap.append(0x26f3, R.drawable.emoji_26f3);\n        sEmojisMap.append(0x26f5, R.drawable.emoji_26f5);\n        sEmojisMap.append(0x26fa, R.drawable.emoji_26fa);\n        sEmojisMap.append(0x26fd, R.drawable.emoji_26fd);\n        sEmojisMap.append(0x2702, R.drawable.emoji_2702);\n        sEmojisMap.append(0x2705, R.drawable.emoji_2705);\n        sEmojisMap.append(0x2708, R.drawable.emoji_2708);\n        sEmojisMap.append(0x2709, R.drawable.emoji_2709);\n        sEmojisMap.append(0x270a, R.drawable.emoji_270a);\n        sEmojisMap.append(0x270b, R.drawable.emoji_270b);\n        sEmojisMap.append(0x270c, R.drawable.emoji_270c);\n        sEmojisMap.append(0x270f, R.drawable.emoji_270f);\n        sEmojisMap.append(0x2712, R.drawable.emoji_2712);\n        sEmojisMap.append(0x2714, R.drawable.emoji_2714);\n        sEmojisMap.append(0x2716, R.drawable.emoji_2716);\n        sEmojisMap.append(0x2728, R.drawable.emoji_2728);\n        sEmojisMap.append(0x2733, R.drawable.emoji_2733);\n        sEmojisMap.append(0x2734, R.drawable.emoji_2734);\n        sEmojisMap.append(0x2744, R.drawable.emoji_2744);\n        sEmojisMap.append(0x2747, R.drawable.emoji_2747);\n        sEmojisMap.append(0x274c, R.drawable.emoji_274c);\n        sEmojisMap.append(0x274e, R.drawable.emoji_274e);\n        sEmojisMap.append(0x2753, R.drawable.emoji_2753);\n        sEmojisMap.append(0x2754, R.drawable.emoji_2754);\n        sEmojisMap.append(0x2755, R.drawable.emoji_2755);\n        sEmojisMap.append(0x2757, R.drawable.emoji_2757);\n        sEmojisMap.append(0x2764, R.drawable.emoji_2764);\n        sEmojisMap.append(0x2795, R.drawable.emoji_2795);\n        sEmojisMap.append(0x2796, R.drawable.emoji_2796);\n        sEmojisMap.append(0x2797, R.drawable.emoji_2797);\n        sEmojisMap.append(0x27a1, R.drawable.emoji_27a1);\n        sEmojisMap.append(0x27b0, R.drawable.emoji_27b0);\n        sEmojisMap.append(0x27bf, R.drawable.emoji_27bf);\n        sEmojisMap.append(0x2934, R.drawable.emoji_2934);\n        sEmojisMap.append(0x2935, R.drawable.emoji_2935);\n        sEmojisMap.append(0x2b05, R.drawable.emoji_2b05);\n        sEmojisMap.append(0x2b06, R.drawable.emoji_2b06);\n        sEmojisMap.append(0x2b07, R.drawable.emoji_2b07);\n        sEmojisMap.append(0x2b1b, R.drawable.emoji_2b1b);\n        sEmojisMap.append(0x2b1c, R.drawable.emoji_2b1c);\n        sEmojisMap.append(0x2b50, R.drawable.emoji_2b50);\n        sEmojisMap.append(0x2b55, R.drawable.emoji_2b55);\n        sEmojisMap.append(0x3030, R.drawable.emoji_3030);\n        sEmojisMap.append(0x303d, R.drawable.emoji_303d);\n        sEmojisMap.append(0x3297, R.drawable.emoji_3297);\n        sEmojisMap.append(0x3299, R.drawable.emoji_3299);\n        sEmojisMap.append(0x1f004, R.drawable.emoji_1f004);\n        sEmojisMap.append(0x1f0cf, R.drawable.emoji_1f0cf);\n        sEmojisMap.append(0x1f170, R.drawable.emoji_1f170);\n        sEmojisMap.append(0x1f171, R.drawable.emoji_1f171);\n        sEmojisMap.append(0x1f17e, R.drawable.emoji_1f17e);\n        sEmojisMap.append(0x1f17f, R.drawable.emoji_1f17f);\n        sEmojisMap.append(0x1f18e, R.drawable.emoji_1f18e);\n        sEmojisMap.append(0x1f191, R.drawable.emoji_1f191);\n        sEmojisMap.append(0x1f192, R.drawable.emoji_1f192);\n        sEmojisMap.append(0x1f193, R.drawable.emoji_1f193);\n        sEmojisMap.append(0x1f194, R.drawable.emoji_1f194);\n        sEmojisMap.append(0x1f195, R.drawable.emoji_1f195);\n        sEmojisMap.append(0x1f196, R.drawable.emoji_1f196);\n        sEmojisMap.append(0x1f197, R.drawable.emoji_1f197);\n        sEmojisMap.append(0x1f198, R.drawable.emoji_1f198);\n        sEmojisMap.append(0x1f199, R.drawable.emoji_1f199);\n        sEmojisMap.append(0x1f19a, R.drawable.emoji_1f19a);\n        sEmojisMap.append(0x1f201, R.drawable.emoji_1f201);\n        sEmojisMap.append(0x1f202, R.drawable.emoji_1f202);\n        sEmojisMap.append(0x1f21a, R.drawable.emoji_1f21a);\n        sEmojisMap.append(0x1f22f, R.drawable.emoji_1f22f);\n        sEmojisMap.append(0x1f232, R.drawable.emoji_1f232);\n        sEmojisMap.append(0x1f233, R.drawable.emoji_1f233);\n        sEmojisMap.append(0x1f234, R.drawable.emoji_1f234);\n        sEmojisMap.append(0x1f235, R.drawable.emoji_1f235);\n        sEmojisMap.append(0x1f236, R.drawable.emoji_1f236);\n        sEmojisMap.append(0x1f237, R.drawable.emoji_1f237);\n        sEmojisMap.append(0x1f238, R.drawable.emoji_1f238);\n        sEmojisMap.append(0x1f239, R.drawable.emoji_1f239);\n        sEmojisMap.append(0x1f23a, R.drawable.emoji_1f23a);\n        sEmojisMap.append(0x1f250, R.drawable.emoji_1f250);\n        sEmojisMap.append(0x1f251, R.drawable.emoji_1f251);\n        sEmojisMap.append(0x1f300, R.drawable.emoji_1f300);\n        sEmojisMap.append(0x1f301, R.drawable.emoji_1f301);\n        sEmojisMap.append(0x1f302, R.drawable.emoji_1f302);\n        sEmojisMap.append(0x1f303, R.drawable.emoji_1f303);\n        sEmojisMap.append(0x1f304, R.drawable.emoji_1f304);\n        sEmojisMap.append(0x1f305, R.drawable.emoji_1f305);\n        sEmojisMap.append(0x1f306, R.drawable.emoji_1f306);\n        sEmojisMap.append(0x1f307, R.drawable.emoji_1f307);\n        sEmojisMap.append(0x1f308, R.drawable.emoji_1f308);\n        sEmojisMap.append(0x1f309, R.drawable.emoji_1f309);\n        sEmojisMap.append(0x1f30a, R.drawable.emoji_1f30a);\n        sEmojisMap.append(0x1f30b, R.drawable.emoji_1f30b);\n        sEmojisMap.append(0x1f30c, R.drawable.emoji_1f30c);\n        sEmojisMap.append(0x1f30d, R.drawable.emoji_1f30d);\n        sEmojisMap.append(0x1f30e, R.drawable.emoji_1f30e);\n        sEmojisMap.append(0x1f30f, R.drawable.emoji_1f30f);\n        sEmojisMap.append(0x1f310, R.drawable.emoji_1f310);\n        sEmojisMap.append(0x1f311, R.drawable.emoji_1f311);\n        sEmojisMap.append(0x1f312, R.drawable.emoji_1f312);\n        sEmojisMap.append(0x1f313, R.drawable.emoji_1f313);\n        sEmojisMap.append(0x1f314, R.drawable.emoji_1f314);\n        sEmojisMap.append(0x1f315, R.drawable.emoji_1f315);\n        sEmojisMap.append(0x1f316, R.drawable.emoji_1f316);\n        sEmojisMap.append(0x1f317, R.drawable.emoji_1f317);\n        sEmojisMap.append(0x1f318, R.drawable.emoji_1f318);\n        sEmojisMap.append(0x1f319, R.drawable.emoji_1f319);\n        sEmojisMap.append(0x1f31a, R.drawable.emoji_1f31a);\n        sEmojisMap.append(0x1f31b, R.drawable.emoji_1f31b);\n        sEmojisMap.append(0x1f31c, R.drawable.emoji_1f31c);\n        sEmojisMap.append(0x1f31d, R.drawable.emoji_1f31d);\n        sEmojisMap.append(0x1f31e, R.drawable.emoji_1f31e);\n        sEmojisMap.append(0x1f31f, R.drawable.emoji_1f31f);\n        sEmojisMap.append(0x1f320, R.drawable.emoji_1f303);\n        sEmojisMap.append(0x1f330, R.drawable.emoji_1f330);\n        sEmojisMap.append(0x1f331, R.drawable.emoji_1f331);\n        sEmojisMap.append(0x1f332, R.drawable.emoji_1f332);\n        sEmojisMap.append(0x1f333, R.drawable.emoji_1f333);\n        sEmojisMap.append(0x1f334, R.drawable.emoji_1f334);\n        sEmojisMap.append(0x1f335, R.drawable.emoji_1f335);\n        sEmojisMap.append(0x1f337, R.drawable.emoji_1f337);\n        sEmojisMap.append(0x1f338, R.drawable.emoji_1f338);\n        sEmojisMap.append(0x1f339, R.drawable.emoji_1f339);\n        sEmojisMap.append(0x1f33a, R.drawable.emoji_1f33a);\n        sEmojisMap.append(0x1f33b, R.drawable.emoji_1f33b);\n        sEmojisMap.append(0x1f33c, R.drawable.emoji_1f33c);\n        sEmojisMap.append(0x1f33d, R.drawable.emoji_1f33d);\n        sEmojisMap.append(0x1f33e, R.drawable.emoji_1f33e);\n        sEmojisMap.append(0x1f33f, R.drawable.emoji_1f33f);\n        sEmojisMap.append(0x1f340, R.drawable.emoji_1f340);\n        sEmojisMap.append(0x1f341, R.drawable.emoji_1f341);\n        sEmojisMap.append(0x1f342, R.drawable.emoji_1f342);\n        sEmojisMap.append(0x1f343, R.drawable.emoji_1f343);\n        sEmojisMap.append(0x1f344, R.drawable.emoji_1f344);\n        sEmojisMap.append(0x1f345, R.drawable.emoji_1f345);\n        sEmojisMap.append(0x1f346, R.drawable.emoji_1f346);\n        sEmojisMap.append(0x1f347, R.drawable.emoji_1f347);\n        sEmojisMap.append(0x1f348, R.drawable.emoji_1f348);\n        sEmojisMap.append(0x1f349, R.drawable.emoji_1f349);\n        sEmojisMap.append(0x1f34a, R.drawable.emoji_1f34a);\n        sEmojisMap.append(0x1f34b, R.drawable.emoji_1f34b);\n        sEmojisMap.append(0x1f34c, R.drawable.emoji_1f34c);\n        sEmojisMap.append(0x1f34d, R.drawable.emoji_1f34d);\n        sEmojisMap.append(0x1f34e, R.drawable.emoji_1f34e);\n        sEmojisMap.append(0x1f34f, R.drawable.emoji_1f34f);\n        sEmojisMap.append(0x1f350, R.drawable.emoji_1f350);\n        sEmojisMap.append(0x1f351, R.drawable.emoji_1f351);\n        sEmojisMap.append(0x1f352, R.drawable.emoji_1f352);\n        sEmojisMap.append(0x1f353, R.drawable.emoji_1f353);\n        sEmojisMap.append(0x1f354, R.drawable.emoji_1f354);\n        sEmojisMap.append(0x1f355, R.drawable.emoji_1f355);\n        sEmojisMap.append(0x1f356, R.drawable.emoji_1f356);\n        sEmojisMap.append(0x1f357, R.drawable.emoji_1f357);\n        sEmojisMap.append(0x1f358, R.drawable.emoji_1f358);\n        sEmojisMap.append(0x1f359, R.drawable.emoji_1f359);\n        sEmojisMap.append(0x1f35a, R.drawable.emoji_1f35a);\n        sEmojisMap.append(0x1f35b, R.drawable.emoji_1f35b);\n        sEmojisMap.append(0x1f35c, R.drawable.emoji_1f35c);\n        sEmojisMap.append(0x1f35d, R.drawable.emoji_1f35d);\n        sEmojisMap.append(0x1f35e, R.drawable.emoji_1f35e);\n        sEmojisMap.append(0x1f35f, R.drawable.emoji_1f35f);\n        sEmojisMap.append(0x1f360, R.drawable.emoji_1f360);\n        sEmojisMap.append(0x1f361, R.drawable.emoji_1f361);\n        sEmojisMap.append(0x1f362, R.drawable.emoji_1f362);\n        sEmojisMap.append(0x1f363, R.drawable.emoji_1f363);\n        sEmojisMap.append(0x1f364, R.drawable.emoji_1f364);\n        sEmojisMap.append(0x1f365, R.drawable.emoji_1f365);\n        sEmojisMap.append(0x1f366, R.drawable.emoji_1f366);\n        sEmojisMap.append(0x1f367, R.drawable.emoji_1f367);\n        sEmojisMap.append(0x1f368, R.drawable.emoji_1f368);\n        sEmojisMap.append(0x1f369, R.drawable.emoji_1f369);\n        sEmojisMap.append(0x1f36a, R.drawable.emoji_1f36a);\n        sEmojisMap.append(0x1f36b, R.drawable.emoji_1f36b);\n        sEmojisMap.append(0x1f36c, R.drawable.emoji_1f36c);\n        sEmojisMap.append(0x1f36d, R.drawable.emoji_1f36d);\n        sEmojisMap.append(0x1f36e, R.drawable.emoji_1f36e);\n        sEmojisMap.append(0x1f36f, R.drawable.emoji_1f36f);\n        sEmojisMap.append(0x1f370, R.drawable.emoji_1f370);\n        sEmojisMap.append(0x1f371, R.drawable.emoji_1f371);\n        sEmojisMap.append(0x1f372, R.drawable.emoji_1f372);\n        sEmojisMap.append(0x1f373, R.drawable.emoji_1f373);\n        sEmojisMap.append(0x1f374, R.drawable.emoji_1f374);\n        sEmojisMap.append(0x1f375, R.drawable.emoji_1f375);\n        sEmojisMap.append(0x1f376, R.drawable.emoji_1f376);\n        sEmojisMap.append(0x1f377, R.drawable.emoji_1f377);\n        sEmojisMap.append(0x1f378, R.drawable.emoji_1f378);\n        sEmojisMap.append(0x1f379, R.drawable.emoji_1f379);\n        sEmojisMap.append(0x1f37a, R.drawable.emoji_1f37a);\n        sEmojisMap.append(0x1f37b, R.drawable.emoji_1f37b);\n        sEmojisMap.append(0x1f37c, R.drawable.emoji_1f37c);\n        sEmojisMap.append(0x1f380, R.drawable.emoji_1f380);\n        sEmojisMap.append(0x1f381, R.drawable.emoji_1f381);\n        sEmojisMap.append(0x1f382, R.drawable.emoji_1f382);\n        sEmojisMap.append(0x1f383, R.drawable.emoji_1f383);\n        sEmojisMap.append(0x1f384, R.drawable.emoji_1f384);\n        sEmojisMap.append(0x1f385, R.drawable.emoji_1f385);\n        sEmojisMap.append(0x1f386, R.drawable.emoji_1f386);\n        sEmojisMap.append(0x1f387, R.drawable.emoji_1f387);\n        sEmojisMap.append(0x1f388, R.drawable.emoji_1f388);\n        sEmojisMap.append(0x1f389, R.drawable.emoji_1f389);\n        sEmojisMap.append(0x1f38a, R.drawable.emoji_1f38a);\n        sEmojisMap.append(0x1f38b, R.drawable.emoji_1f38b);\n        sEmojisMap.append(0x1f38c, R.drawable.emoji_1f38c);\n        sEmojisMap.append(0x1f38d, R.drawable.emoji_1f38d);\n        sEmojisMap.append(0x1f38e, R.drawable.emoji_1f38e);\n        sEmojisMap.append(0x1f38f, R.drawable.emoji_1f38f);\n        sEmojisMap.append(0x1f390, R.drawable.emoji_1f390);\n        sEmojisMap.append(0x1f391, R.drawable.emoji_1f391);\n        sEmojisMap.append(0x1f392, R.drawable.emoji_1f392);\n        sEmojisMap.append(0x1f393, R.drawable.emoji_1f393);\n        sEmojisMap.append(0x1f3a0, R.drawable.emoji_1f3a0);\n        sEmojisMap.append(0x1f3a1, R.drawable.emoji_1f3a1);\n        sEmojisMap.append(0x1f3a2, R.drawable.emoji_1f3a2);\n        sEmojisMap.append(0x1f3a3, R.drawable.emoji_1f3a3);\n        sEmojisMap.append(0x1f3a4, R.drawable.emoji_1f3a4);\n        sEmojisMap.append(0x1f3a5, R.drawable.emoji_1f3a5);\n        sEmojisMap.append(0x1f3a6, R.drawable.emoji_1f3a6);\n        sEmojisMap.append(0x1f3a7, R.drawable.emoji_1f3a7);\n        sEmojisMap.append(0x1f3a8, R.drawable.emoji_1f3a8);\n        sEmojisMap.append(0x1f3a9, R.drawable.emoji_1f3a9);\n        sEmojisMap.append(0x1f3aa, R.drawable.emoji_1f3aa);\n        sEmojisMap.append(0x1f3ab, R.drawable.emoji_1f3ab);\n        sEmojisMap.append(0x1f3ac, R.drawable.emoji_1f3ac);\n        sEmojisMap.append(0x1f3ad, R.drawable.emoji_1f3ad);\n        sEmojisMap.append(0x1f3ae, R.drawable.emoji_1f3ae);\n        sEmojisMap.append(0x1f3af, R.drawable.emoji_1f3af);\n        sEmojisMap.append(0x1f3b0, R.drawable.emoji_1f3b0);\n        sEmojisMap.append(0x1f3b1, R.drawable.emoji_1f3b1);\n        sEmojisMap.append(0x1f3b2, R.drawable.emoji_1f3b2);\n        sEmojisMap.append(0x1f3b3, R.drawable.emoji_1f3b3);\n        sEmojisMap.append(0x1f3b4, R.drawable.emoji_1f3b4);\n        sEmojisMap.append(0x1f3b5, R.drawable.emoji_1f3b5);\n        sEmojisMap.append(0x1f3b6, R.drawable.emoji_1f3b6);\n        sEmojisMap.append(0x1f3b7, R.drawable.emoji_1f3b7);\n        sEmojisMap.append(0x1f3b8, R.drawable.emoji_1f3b8);\n        sEmojisMap.append(0x1f3b9, R.drawable.emoji_1f3b9);\n        sEmojisMap.append(0x1f3ba, R.drawable.emoji_1f3ba);\n        sEmojisMap.append(0x1f3bb, R.drawable.emoji_1f3bb);\n        sEmojisMap.append(0x1f3bc, R.drawable.emoji_1f3bc);\n        sEmojisMap.append(0x1f3bd, R.drawable.emoji_1f3bd);\n        sEmojisMap.append(0x1f3be, R.drawable.emoji_1f3be);\n        sEmojisMap.append(0x1f3bf, R.drawable.emoji_1f3bf);\n        sEmojisMap.append(0x1f3c0, R.drawable.emoji_1f3c0);\n        sEmojisMap.append(0x1f3c1, R.drawable.emoji_1f3c1);\n        sEmojisMap.append(0x1f3c2, R.drawable.emoji_1f3c2);\n        sEmojisMap.append(0x1f3c3, R.drawable.emoji_1f3c3);\n        sEmojisMap.append(0x1f3c4, R.drawable.emoji_1f3c4);\n        sEmojisMap.append(0x1f3c6, R.drawable.emoji_1f3c6);\n        sEmojisMap.append(0x1f3c7, R.drawable.emoji_1f3c7);\n        sEmojisMap.append(0x1f3c8, R.drawable.emoji_1f3c8);\n        sEmojisMap.append(0x1f3c9, R.drawable.emoji_1f3c9);\n        sEmojisMap.append(0x1f3ca, R.drawable.emoji_1f3ca);\n        sEmojisMap.append(0x1f3e0, R.drawable.emoji_1f3e0);\n        sEmojisMap.append(0x1f3e1, R.drawable.emoji_1f3e1);\n        sEmojisMap.append(0x1f3e2, R.drawable.emoji_1f3e2);\n        sEmojisMap.append(0x1f3e3, R.drawable.emoji_1f3e3);\n        sEmojisMap.append(0x1f3e4, R.drawable.emoji_1f3e4);\n        sEmojisMap.append(0x1f3e5, R.drawable.emoji_1f3e5);\n        sEmojisMap.append(0x1f3e6, R.drawable.emoji_1f3e6);\n        sEmojisMap.append(0x1f3e7, R.drawable.emoji_1f3e7);\n        sEmojisMap.append(0x1f3e8, R.drawable.emoji_1f3e8);\n        sEmojisMap.append(0x1f3e9, R.drawable.emoji_1f3e9);\n        sEmojisMap.append(0x1f3ea, R.drawable.emoji_1f3ea);\n        sEmojisMap.append(0x1f3eb, R.drawable.emoji_1f3eb);\n        sEmojisMap.append(0x1f3ec, R.drawable.emoji_1f3ec);\n        sEmojisMap.append(0x1f3ed, R.drawable.emoji_1f3ed);\n        sEmojisMap.append(0x1f3ee, R.drawable.emoji_1f3ee);\n        sEmojisMap.append(0x1f3ef, R.drawable.emoji_1f3ef);\n        sEmojisMap.append(0x1f3f0, R.drawable.emoji_1f3f0);\n        sEmojisMap.append(0x1f400, R.drawable.emoji_1f400);\n        sEmojisMap.append(0x1f401, R.drawable.emoji_1f401);\n        sEmojisMap.append(0x1f402, R.drawable.emoji_1f402);\n        sEmojisMap.append(0x1f403, R.drawable.emoji_1f403);\n        sEmojisMap.append(0x1f404, R.drawable.emoji_1f404);\n        sEmojisMap.append(0x1f405, R.drawable.emoji_1f405);\n        sEmojisMap.append(0x1f406, R.drawable.emoji_1f406);\n        sEmojisMap.append(0x1f407, R.drawable.emoji_1f407);\n        sEmojisMap.append(0x1f408, R.drawable.emoji_1f408);\n        sEmojisMap.append(0x1f409, R.drawable.emoji_1f409);\n        sEmojisMap.append(0x1f40a, R.drawable.emoji_1f40a);\n        sEmojisMap.append(0x1f40b, R.drawable.emoji_1f40b);\n        sEmojisMap.append(0x1f40c, R.drawable.emoji_1f40c);\n        sEmojisMap.append(0x1f40d, R.drawable.emoji_1f40d);\n        sEmojisMap.append(0x1f40e, R.drawable.emoji_1f40e);\n        sEmojisMap.append(0x1f40f, R.drawable.emoji_1f40f);\n        sEmojisMap.append(0x1f410, R.drawable.emoji_1f410);\n        sEmojisMap.append(0x1f411, R.drawable.emoji_1f411);\n        sEmojisMap.append(0x1f412, R.drawable.emoji_1f412);\n        sEmojisMap.append(0x1f413, R.drawable.emoji_1f413);\n        sEmojisMap.append(0x1f414, R.drawable.emoji_1f414);\n        sEmojisMap.append(0x1f415, R.drawable.emoji_1f415);\n        sEmojisMap.append(0x1f416, R.drawable.emoji_1f416);\n        sEmojisMap.append(0x1f417, R.drawable.emoji_1f417);\n        sEmojisMap.append(0x1f418, R.drawable.emoji_1f418);\n        sEmojisMap.append(0x1f419, R.drawable.emoji_1f419);\n        sEmojisMap.append(0x1f41a, R.drawable.emoji_1f41a);\n        sEmojisMap.append(0x1f41b, R.drawable.emoji_1f41b);\n        sEmojisMap.append(0x1f41c, R.drawable.emoji_1f41c);\n        sEmojisMap.append(0x1f41d, R.drawable.emoji_1f41d);\n        sEmojisMap.append(0x1f41e, R.drawable.emoji_1f41e);\n        sEmojisMap.append(0x1f41f, R.drawable.emoji_1f41f);\n        sEmojisMap.append(0x1f420, R.drawable.emoji_1f420);\n        sEmojisMap.append(0x1f421, R.drawable.emoji_1f421);\n        sEmojisMap.append(0x1f422, R.drawable.emoji_1f422);\n        sEmojisMap.append(0x1f423, R.drawable.emoji_1f423);\n        sEmojisMap.append(0x1f424, R.drawable.emoji_1f424);\n        sEmojisMap.append(0x1f425, R.drawable.emoji_1f425);\n        sEmojisMap.append(0x1f426, R.drawable.emoji_1f426);\n        sEmojisMap.append(0x1f427, R.drawable.emoji_1f427);\n        sEmojisMap.append(0x1f428, R.drawable.emoji_1f428);\n        sEmojisMap.append(0x1f429, R.drawable.emoji_1f429);\n        sEmojisMap.append(0x1f42a, R.drawable.emoji_1f42a);\n        sEmojisMap.append(0x1f42b, R.drawable.emoji_1f42b);\n        sEmojisMap.append(0x1f42c, R.drawable.emoji_1f42c);\n        sEmojisMap.append(0x1f42d, R.drawable.emoji_1f42d);\n        sEmojisMap.append(0x1f42e, R.drawable.emoji_1f42e);\n        sEmojisMap.append(0x1f42f, R.drawable.emoji_1f42f);\n        sEmojisMap.append(0x1f430, R.drawable.emoji_1f430);\n        sEmojisMap.append(0x1f431, R.drawable.emoji_1f431);\n        sEmojisMap.append(0x1f432, R.drawable.emoji_1f432);\n        sEmojisMap.append(0x1f433, R.drawable.emoji_1f433);\n        sEmojisMap.append(0x1f434, R.drawable.emoji_1f434);\n        sEmojisMap.append(0x1f435, R.drawable.emoji_1f435);\n        sEmojisMap.append(0x1f436, R.drawable.emoji_1f436);\n        sEmojisMap.append(0x1f437, R.drawable.emoji_1f437);\n        sEmojisMap.append(0x1f438, R.drawable.emoji_1f438);\n        sEmojisMap.append(0x1f439, R.drawable.emoji_1f439);\n        sEmojisMap.append(0x1f43a, R.drawable.emoji_1f43a);\n        sEmojisMap.append(0x1f43b, R.drawable.emoji_1f43b);\n        sEmojisMap.append(0x1f43c, R.drawable.emoji_1f43c);\n        sEmojisMap.append(0x1f43d, R.drawable.emoji_1f43d);\n        sEmojisMap.append(0x1f43e, R.drawable.emoji_1f43e);\n        sEmojisMap.append(0x1f440, R.drawable.emoji_1f440);\n        sEmojisMap.append(0x1f442, R.drawable.emoji_1f442);\n        sEmojisMap.append(0x1f443, R.drawable.emoji_1f443);\n        sEmojisMap.append(0x1f444, R.drawable.emoji_1f444);\n        sEmojisMap.append(0x1f445, R.drawable.emoji_1f445);\n        sEmojisMap.append(0x1f446, R.drawable.emoji_1f446);\n        sEmojisMap.append(0x1f447, R.drawable.emoji_1f447);\n        sEmojisMap.append(0x1f448, R.drawable.emoji_1f448);\n        sEmojisMap.append(0x1f449, R.drawable.emoji_1f449);\n        sEmojisMap.append(0x1f44a, R.drawable.emoji_1f44a);\n        sEmojisMap.append(0x1f44b, R.drawable.emoji_1f44b);\n        sEmojisMap.append(0x1f44c, R.drawable.emoji_1f44c);\n        sEmojisMap.append(0x1f44d, R.drawable.emoji_1f44d);\n        sEmojisMap.append(0x1f44e, R.drawable.emoji_1f44e);\n        sEmojisMap.append(0x1f44f, R.drawable.emoji_1f44f);\n        sEmojisMap.append(0x1f450, R.drawable.emoji_1f450);\n        sEmojisMap.append(0x1f451, R.drawable.emoji_1f451);\n        sEmojisMap.append(0x1f452, R.drawable.emoji_1f452);\n        sEmojisMap.append(0x1f453, R.drawable.emoji_1f453);\n        sEmojisMap.append(0x1f454, R.drawable.emoji_1f454);\n        sEmojisMap.append(0x1f455, R.drawable.emoji_1f455);\n        sEmojisMap.append(0x1f456, R.drawable.emoji_1f456);\n        sEmojisMap.append(0x1f457, R.drawable.emoji_1f457);\n        sEmojisMap.append(0x1f458, R.drawable.emoji_1f458);\n        sEmojisMap.append(0x1f459, R.drawable.emoji_1f459);\n        sEmojisMap.append(0x1f45a, R.drawable.emoji_1f45a);\n        sEmojisMap.append(0x1f45b, R.drawable.emoji_1f45b);\n        sEmojisMap.append(0x1f45c, R.drawable.emoji_1f45c);\n        sEmojisMap.append(0x1f45d, R.drawable.emoji_1f45d);\n        sEmojisMap.append(0x1f45e, R.drawable.emoji_1f45e);\n        sEmojisMap.append(0x1f45f, R.drawable.emoji_1f45f);\n        sEmojisMap.append(0x1f460, R.drawable.emoji_1f460);\n        sEmojisMap.append(0x1f461, R.drawable.emoji_1f461);\n        sEmojisMap.append(0x1f462, R.drawable.emoji_1f462);\n        sEmojisMap.append(0x1f463, R.drawable.emoji_1f463);\n        sEmojisMap.append(0x1f464, R.drawable.emoji_1f464);\n        sEmojisMap.append(0x1f465, R.drawable.emoji_1f465);\n        sEmojisMap.append(0x1f466, R.drawable.emoji_1f466);\n        sEmojisMap.append(0x1f467, R.drawable.emoji_1f467);\n        sEmojisMap.append(0x1f468, R.drawable.emoji_1f468);\n        sEmojisMap.append(0x1f469, R.drawable.emoji_1f469);\n        sEmojisMap.append(0x1f46a, R.drawable.emoji_1f46a);\n        sEmojisMap.append(0x1f46b, R.drawable.emoji_1f46b);\n        sEmojisMap.append(0x1f46c, R.drawable.emoji_1f46c);\n        sEmojisMap.append(0x1f46d, R.drawable.emoji_1f46d);\n        sEmojisMap.append(0x1f46e, R.drawable.emoji_1f46e);\n        sEmojisMap.append(0x1f46f, R.drawable.emoji_1f46f);\n        sEmojisMap.append(0x1f470, R.drawable.emoji_1f470);\n        sEmojisMap.append(0x1f471, R.drawable.emoji_1f471);\n        sEmojisMap.append(0x1f472, R.drawable.emoji_1f472);\n        sEmojisMap.append(0x1f473, R.drawable.emoji_1f473);\n        sEmojisMap.append(0x1f474, R.drawable.emoji_1f474);\n        sEmojisMap.append(0x1f475, R.drawable.emoji_1f475);\n        sEmojisMap.append(0x1f476, R.drawable.emoji_1f476);\n        sEmojisMap.append(0x1f477, R.drawable.emoji_1f477);\n        sEmojisMap.append(0x1f478, R.drawable.emoji_1f478);\n        sEmojisMap.append(0x1f479, R.drawable.emoji_1f479);\n        sEmojisMap.append(0x1f47a, R.drawable.emoji_1f47a);\n        sEmojisMap.append(0x1f47b, R.drawable.emoji_1f47b);\n        sEmojisMap.append(0x1f47c, R.drawable.emoji_1f47c);\n        sEmojisMap.append(0x1f47d, R.drawable.emoji_1f47d);\n        sEmojisMap.append(0x1f47e, R.drawable.emoji_1f47e);\n        sEmojisMap.append(0x1f47f, R.drawable.emoji_1f47f);\n        sEmojisMap.append(0x1f480, R.drawable.emoji_1f480);\n        sEmojisMap.append(0x1f481, R.drawable.emoji_1f481);\n        sEmojisMap.append(0x1f482, R.drawable.emoji_1f482);\n        sEmojisMap.append(0x1f483, R.drawable.emoji_1f483);\n        sEmojisMap.append(0x1f484, R.drawable.emoji_1f484);\n        sEmojisMap.append(0x1f485, R.drawable.emoji_1f485);\n        sEmojisMap.append(0x1f486, R.drawable.emoji_1f486);\n        sEmojisMap.append(0x1f487, R.drawable.emoji_1f487);\n        sEmojisMap.append(0x1f488, R.drawable.emoji_1f488);\n        sEmojisMap.append(0x1f489, R.drawable.emoji_1f489);\n        sEmojisMap.append(0x1f48a, R.drawable.emoji_1f48a);\n        sEmojisMap.append(0x1f48b, R.drawable.emoji_1f48b);\n        sEmojisMap.append(0x1f48c, R.drawable.emoji_1f48c);\n        sEmojisMap.append(0x1f48d, R.drawable.emoji_1f48d);\n        sEmojisMap.append(0x1f48e, R.drawable.emoji_1f48e);\n        sEmojisMap.append(0x1f48f, R.drawable.emoji_1f48f);\n        sEmojisMap.append(0x1f490, R.drawable.emoji_1f490);\n        sEmojisMap.append(0x1f491, R.drawable.emoji_1f491);\n        sEmojisMap.append(0x1f492, R.drawable.emoji_1f492);\n        sEmojisMap.append(0x1f493, R.drawable.emoji_1f493);\n        sEmojisMap.append(0x1f494, R.drawable.emoji_1f494);\n        sEmojisMap.append(0x1f495, R.drawable.emoji_1f495);\n        sEmojisMap.append(0x1f496, R.drawable.emoji_1f496);\n        sEmojisMap.append(0x1f497, R.drawable.emoji_1f497);\n        sEmojisMap.append(0x1f498, R.drawable.emoji_1f498);\n        sEmojisMap.append(0x1f499, R.drawable.emoji_1f499);\n        sEmojisMap.append(0x1f49a, R.drawable.emoji_1f49a);\n        sEmojisMap.append(0x1f49b, R.drawable.emoji_1f49b);\n        sEmojisMap.append(0x1f49c, R.drawable.emoji_1f49c);\n        sEmojisMap.append(0x1f49d, R.drawable.emoji_1f49d);\n        sEmojisMap.append(0x1f49e, R.drawable.emoji_1f49e);\n        sEmojisMap.append(0x1f49f, R.drawable.emoji_1f49f);\n        sEmojisMap.append(0x1f4a0, R.drawable.emoji_1f4a0);\n        sEmojisMap.append(0x1f4a1, R.drawable.emoji_1f4a1);\n        sEmojisMap.append(0x1f4a2, R.drawable.emoji_1f4a2);\n        sEmojisMap.append(0x1f4a3, R.drawable.emoji_1f4a3);\n        sEmojisMap.append(0x1f4a4, R.drawable.emoji_1f4a4);\n        sEmojisMap.append(0x1f4a5, R.drawable.emoji_1f4a5);\n        sEmojisMap.append(0x1f4a6, R.drawable.emoji_1f4a6);\n        sEmojisMap.append(0x1f4a7, R.drawable.emoji_1f4a7);\n        sEmojisMap.append(0x1f4a8, R.drawable.emoji_1f4a8);\n        sEmojisMap.append(0x1f4a9, R.drawable.emoji_1f4a9);\n        sEmojisMap.append(0x1f4aa, R.drawable.emoji_1f4aa);\n        sEmojisMap.append(0x1f4ab, R.drawable.emoji_1f4ab);\n        sEmojisMap.append(0x1f4ac, R.drawable.emoji_1f4ac);\n        sEmojisMap.append(0x1f4ad, R.drawable.emoji_1f4ad);\n        sEmojisMap.append(0x1f4ae, R.drawable.emoji_1f4ae);\n        sEmojisMap.append(0x1f4af, R.drawable.emoji_1f4af);\n        sEmojisMap.append(0x1f4b0, R.drawable.emoji_1f4b0);\n        sEmojisMap.append(0x1f4b1, R.drawable.emoji_1f4b1);\n        sEmojisMap.append(0x1f4b2, R.drawable.emoji_1f4b2);\n        sEmojisMap.append(0x1f4b3, R.drawable.emoji_1f4b3);\n        sEmojisMap.append(0x1f4b4, R.drawable.emoji_1f4b4);\n        sEmojisMap.append(0x1f4b5, R.drawable.emoji_1f4b5);\n        sEmojisMap.append(0x1f4b6, R.drawable.emoji_1f4b6);\n        sEmojisMap.append(0x1f4b7, R.drawable.emoji_1f4b7);\n        sEmojisMap.append(0x1f4b8, R.drawable.emoji_1f4b8);\n        sEmojisMap.append(0x1f4b9, R.drawable.emoji_1f4b9);\n        sEmojisMap.append(0x1f4ba, R.drawable.emoji_1f4ba);\n        sEmojisMap.append(0x1f4bb, R.drawable.emoji_1f4bb);\n        sEmojisMap.append(0x1f4bc, R.drawable.emoji_1f4bc);\n        sEmojisMap.append(0x1f4bd, R.drawable.emoji_1f4bd);\n        sEmojisMap.append(0x1f4be, R.drawable.emoji_1f4be);\n        sEmojisMap.append(0x1f4bf, R.drawable.emoji_1f4bf);\n        sEmojisMap.append(0x1f4c0, R.drawable.emoji_1f4c0);\n        sEmojisMap.append(0x1f4c1, R.drawable.emoji_1f4c1);\n        sEmojisMap.append(0x1f4c2, R.drawable.emoji_1f4c2);\n        sEmojisMap.append(0x1f4c3, R.drawable.emoji_1f4c3);\n        sEmojisMap.append(0x1f4c4, R.drawable.emoji_1f4c4);\n        sEmojisMap.append(0x1f4c5, R.drawable.emoji_1f4c5);\n        sEmojisMap.append(0x1f4c6, R.drawable.emoji_1f4c6);\n        sEmojisMap.append(0x1f4c7, R.drawable.emoji_1f4c7);\n        sEmojisMap.append(0x1f4c8, R.drawable.emoji_1f4c8);\n        sEmojisMap.append(0x1f4c9, R.drawable.emoji_1f4c9);\n        sEmojisMap.append(0x1f4ca, R.drawable.emoji_1f4ca);\n        sEmojisMap.append(0x1f4cb, R.drawable.emoji_1f4cb);\n        sEmojisMap.append(0x1f4cc, R.drawable.emoji_1f4cc);\n        sEmojisMap.append(0x1f4cd, R.drawable.emoji_1f4cd);\n        sEmojisMap.append(0x1f4ce, R.drawable.emoji_1f4ce);\n        sEmojisMap.append(0x1f4cf, R.drawable.emoji_1f4cf);\n        sEmojisMap.append(0x1f4d0, R.drawable.emoji_1f4d0);\n        sEmojisMap.append(0x1f4d1, R.drawable.emoji_1f4d1);\n        sEmojisMap.append(0x1f4d2, R.drawable.emoji_1f4d2);\n        sEmojisMap.append(0x1f4d3, R.drawable.emoji_1f4d3);\n        sEmojisMap.append(0x1f4d4, R.drawable.emoji_1f4d4);\n        sEmojisMap.append(0x1f4d5, R.drawable.emoji_1f4d5);\n        sEmojisMap.append(0x1f4d6, R.drawable.emoji_1f4d6);\n        sEmojisMap.append(0x1f4d7, R.drawable.emoji_1f4d7);\n        sEmojisMap.append(0x1f4d8, R.drawable.emoji_1f4d8);\n        sEmojisMap.append(0x1f4d9, R.drawable.emoji_1f4d9);\n        sEmojisMap.append(0x1f4da, R.drawable.emoji_1f4da);\n        sEmojisMap.append(0x1f4db, R.drawable.emoji_1f4db);\n        sEmojisMap.append(0x1f4dc, R.drawable.emoji_1f4dc);\n        sEmojisMap.append(0x1f4dd, R.drawable.emoji_1f4dd);\n        sEmojisMap.append(0x1f4de, R.drawable.emoji_1f4de);\n        sEmojisMap.append(0x1f4df, R.drawable.emoji_1f4df);\n        sEmojisMap.append(0x1f4e0, R.drawable.emoji_1f4e0);\n        sEmojisMap.append(0x1f4e1, R.drawable.emoji_1f4e1);\n        sEmojisMap.append(0x1f4e2, R.drawable.emoji_1f4e2);\n        sEmojisMap.append(0x1f4e3, R.drawable.emoji_1f4e3);\n        sEmojisMap.append(0x1f4e4, R.drawable.emoji_1f4e4);\n        sEmojisMap.append(0x1f4e5, R.drawable.emoji_1f4e5);\n        sEmojisMap.append(0x1f4e6, R.drawable.emoji_1f4e6);\n        sEmojisMap.append(0x1f4e7, R.drawable.emoji_1f4e7);\n        sEmojisMap.append(0x1f4e8, R.drawable.emoji_1f4e8);\n        sEmojisMap.append(0x1f4e9, R.drawable.emoji_1f4e9);\n        sEmojisMap.append(0x1f4ea, R.drawable.emoji_1f4ea);\n        sEmojisMap.append(0x1f4eb, R.drawable.emoji_1f4eb);\n        sEmojisMap.append(0x1f4ec, R.drawable.emoji_1f4ec);\n        sEmojisMap.append(0x1f4ed, R.drawable.emoji_1f4ed);\n        sEmojisMap.append(0x1f4ee, R.drawable.emoji_1f4ee);\n        sEmojisMap.append(0x1f4ef, R.drawable.emoji_1f4ef);\n        sEmojisMap.append(0x1f4f0, R.drawable.emoji_1f4f0);\n        sEmojisMap.append(0x1f4f1, R.drawable.emoji_1f4f1);\n        sEmojisMap.append(0x1f4f2, R.drawable.emoji_1f4f2);\n        sEmojisMap.append(0x1f4f3, R.drawable.emoji_1f4f3);\n        sEmojisMap.append(0x1f4f4, R.drawable.emoji_1f4f4);\n        sEmojisMap.append(0x1f4f5, R.drawable.emoji_1f4f5);\n        sEmojisMap.append(0x1f4f6, R.drawable.emoji_1f4f6);\n        sEmojisMap.append(0x1f4f7, R.drawable.emoji_1f4f7);\n        sEmojisMap.append(0x1f4f9, R.drawable.emoji_1f4f9);\n        sEmojisMap.append(0x1f4fa, R.drawable.emoji_1f4fa);\n        sEmojisMap.append(0x1f4fb, R.drawable.emoji_1f4fb);\n        sEmojisMap.append(0x1f4fc, R.drawable.emoji_1f4fc);\n        sEmojisMap.append(0x1f500, R.drawable.emoji_1f500);\n        sEmojisMap.append(0x1f501, R.drawable.emoji_1f501);\n        sEmojisMap.append(0x1f502, R.drawable.emoji_1f502);\n        sEmojisMap.append(0x1f503, R.drawable.emoji_1f503);\n        sEmojisMap.append(0x1f504, R.drawable.emoji_1f504);\n        sEmojisMap.append(0x1f505, R.drawable.emoji_1f505);\n        sEmojisMap.append(0x1f506, R.drawable.emoji_1f506);\n        sEmojisMap.append(0x1f507, R.drawable.emoji_1f507);\n        sEmojisMap.append(0x1f508, R.drawable.emoji_1f508);\n        sEmojisMap.append(0x1f509, R.drawable.emoji_1f509);\n        sEmojisMap.append(0x1f50a, R.drawable.emoji_1f50a);\n        sEmojisMap.append(0x1f50b, R.drawable.emoji_1f50b);\n        sEmojisMap.append(0x1f50c, R.drawable.emoji_1f50c);\n        sEmojisMap.append(0x1f50d, R.drawable.emoji_1f50d);\n        sEmojisMap.append(0x1f50e, R.drawable.emoji_1f50e);\n        sEmojisMap.append(0x1f50f, R.drawable.emoji_1f50f);\n        sEmojisMap.append(0x1f510, R.drawable.emoji_1f510);\n        sEmojisMap.append(0x1f511, R.drawable.emoji_1f511);\n        sEmojisMap.append(0x1f512, R.drawable.emoji_1f512);\n        sEmojisMap.append(0x1f513, R.drawable.emoji_1f513);\n        sEmojisMap.append(0x1f514, R.drawable.emoji_1f514);\n        sEmojisMap.append(0x1f515, R.drawable.emoji_1f515);\n        sEmojisMap.append(0x1f516, R.drawable.emoji_1f516);\n        sEmojisMap.append(0x1f517, R.drawable.emoji_1f517);\n        sEmojisMap.append(0x1f518, R.drawable.emoji_1f518);\n        sEmojisMap.append(0x1f519, R.drawable.emoji_1f519);\n        sEmojisMap.append(0x1f51a, R.drawable.emoji_1f51a);\n        sEmojisMap.append(0x1f51b, R.drawable.emoji_1f51b);\n        sEmojisMap.append(0x1f51c, R.drawable.emoji_1f51c);\n        sEmojisMap.append(0x1f51d, R.drawable.emoji_1f51d);\n        sEmojisMap.append(0x1f51e, R.drawable.emoji_1f51e);\n        sEmojisMap.append(0x1f51f, R.drawable.emoji_1f51f);\n        sEmojisMap.append(0x1f520, R.drawable.emoji_1f520);\n        sEmojisMap.append(0x1f521, R.drawable.emoji_1f521);\n        sEmojisMap.append(0x1f522, R.drawable.emoji_1f522);\n        sEmojisMap.append(0x1f523, R.drawable.emoji_1f523);\n        sEmojisMap.append(0x1f524, R.drawable.emoji_1f524);\n        sEmojisMap.append(0x1f525, R.drawable.emoji_1f525);\n        sEmojisMap.append(0x1f526, R.drawable.emoji_1f526);\n        sEmojisMap.append(0x1f527, R.drawable.emoji_1f527);\n        sEmojisMap.append(0x1f528, R.drawable.emoji_1f528);\n        sEmojisMap.append(0x1f529, R.drawable.emoji_1f529);\n        sEmojisMap.append(0x1f52a, R.drawable.emoji_1f52a);\n        sEmojisMap.append(0x1f52b, R.drawable.emoji_1f52b);\n        sEmojisMap.append(0x1f52c, R.drawable.emoji_1f52c);\n        sEmojisMap.append(0x1f52d, R.drawable.emoji_1f52d);\n        sEmojisMap.append(0x1f52e, R.drawable.emoji_1f52e);\n        sEmojisMap.append(0x1f52f, R.drawable.emoji_1f52f);\n        sEmojisMap.append(0x1f530, R.drawable.emoji_1f530);\n        sEmojisMap.append(0x1f531, R.drawable.emoji_1f531);\n        sEmojisMap.append(0x1f532, R.drawable.emoji_1f532);\n        sEmojisMap.append(0x1f533, R.drawable.emoji_1f533);\n        sEmojisMap.append(0x1f534, R.drawable.emoji_1f534);\n        sEmojisMap.append(0x1f535, R.drawable.emoji_1f535);\n        sEmojisMap.append(0x1f536, R.drawable.emoji_1f536);\n        sEmojisMap.append(0x1f537, R.drawable.emoji_1f537);\n        sEmojisMap.append(0x1f538, R.drawable.emoji_1f538);\n        sEmojisMap.append(0x1f539, R.drawable.emoji_1f539);\n        sEmojisMap.append(0x1f53a, R.drawable.emoji_1f53a);\n        sEmojisMap.append(0x1f53b, R.drawable.emoji_1f53b);\n        sEmojisMap.append(0x1f53c, R.drawable.emoji_1f53c);\n        sEmojisMap.append(0x1f53d, R.drawable.emoji_1f53d);\n        sEmojisMap.append(0x1f550, R.drawable.emoji_1f550);\n        sEmojisMap.append(0x1f551, R.drawable.emoji_1f551);\n        sEmojisMap.append(0x1f552, R.drawable.emoji_1f552);\n        sEmojisMap.append(0x1f553, R.drawable.emoji_1f553);\n        sEmojisMap.append(0x1f554, R.drawable.emoji_1f554);\n        sEmojisMap.append(0x1f555, R.drawable.emoji_1f555);\n        sEmojisMap.append(0x1f556, R.drawable.emoji_1f556);\n        sEmojisMap.append(0x1f557, R.drawable.emoji_1f557);\n        sEmojisMap.append(0x1f558, R.drawable.emoji_1f558);\n        sEmojisMap.append(0x1f559, R.drawable.emoji_1f559);\n        sEmojisMap.append(0x1f55a, R.drawable.emoji_1f55a);\n        sEmojisMap.append(0x1f55b, R.drawable.emoji_1f55b);\n        sEmojisMap.append(0x1f55c, R.drawable.emoji_1f55c);\n        sEmojisMap.append(0x1f55d, R.drawable.emoji_1f55d);\n        sEmojisMap.append(0x1f55e, R.drawable.emoji_1f55e);\n        sEmojisMap.append(0x1f55f, R.drawable.emoji_1f55f);\n        sEmojisMap.append(0x1f560, R.drawable.emoji_1f560);\n        sEmojisMap.append(0x1f561, R.drawable.emoji_1f561);\n        sEmojisMap.append(0x1f562, R.drawable.emoji_1f562);\n        sEmojisMap.append(0x1f563, R.drawable.emoji_1f563);\n        sEmojisMap.append(0x1f564, R.drawable.emoji_1f564);\n        sEmojisMap.append(0x1f565, R.drawable.emoji_1f565);\n        sEmojisMap.append(0x1f566, R.drawable.emoji_1f566);\n        sEmojisMap.append(0x1f567, R.drawable.emoji_1f567);\n        sEmojisMap.append(0x1f5fb, R.drawable.emoji_1f5fb);\n        sEmojisMap.append(0x1f5fc, R.drawable.emoji_1f5fc);\n        sEmojisMap.append(0x1f5fd, R.drawable.emoji_1f5fd);\n        sEmojisMap.append(0x1f5fe, R.drawable.emoji_1f5fe);\n        sEmojisMap.append(0x1f5ff, R.drawable.emoji_1f5ff);\n        sEmojisMap.append(0x1f600, R.drawable.emoji_1f600);\n        sEmojisMap.append(0x1f601, R.drawable.emoji_1f601);\n        sEmojisMap.append(0x1f602, R.drawable.emoji_1f602);\n        sEmojisMap.append(0x1f603, R.drawable.emoji_1f603);\n        sEmojisMap.append(0x1f604, R.drawable.emoji_1f604);\n        sEmojisMap.append(0x1f605, R.drawable.emoji_1f605);\n        sEmojisMap.append(0x1f606, R.drawable.emoji_1f606);\n        sEmojisMap.append(0x1f607, R.drawable.emoji_1f607);\n        sEmojisMap.append(0x1f608, R.drawable.emoji_1f608);\n        sEmojisMap.append(0x1f609, R.drawable.emoji_1f609);\n        sEmojisMap.append(0x1f60a, R.drawable.emoji_1f60a);\n        sEmojisMap.append(0x1f60b, R.drawable.emoji_1f60b);\n        sEmojisMap.append(0x1f60c, R.drawable.emoji_1f60c);\n        sEmojisMap.append(0x1f60d, R.drawable.emoji_1f60d);\n        sEmojisMap.append(0x1f60e, R.drawable.emoji_1f60e);\n        sEmojisMap.append(0x1f60f, R.drawable.emoji_1f60f);\n        sEmojisMap.append(0x1f610, R.drawable.emoji_1f610);\n        sEmojisMap.append(0x1f611, R.drawable.emoji_1f611);\n        sEmojisMap.append(0x1f612, R.drawable.emoji_1f612);\n        sEmojisMap.append(0x1f613, R.drawable.emoji_1f613);\n        sEmojisMap.append(0x1f614, R.drawable.emoji_1f614);\n        sEmojisMap.append(0x1f615, R.drawable.emoji_1f615);\n        sEmojisMap.append(0x1f616, R.drawable.emoji_1f616);\n        sEmojisMap.append(0x1f617, R.drawable.emoji_1f617);\n        sEmojisMap.append(0x1f618, R.drawable.emoji_1f618);\n        sEmojisMap.append(0x1f619, R.drawable.emoji_1f619);\n        sEmojisMap.append(0x1f61a, R.drawable.emoji_1f61a);\n        sEmojisMap.append(0x1f61b, R.drawable.emoji_1f61b);\n        sEmojisMap.append(0x1f61c, R.drawable.emoji_1f61c);\n        sEmojisMap.append(0x1f61d, R.drawable.emoji_1f61d);\n        sEmojisMap.append(0x1f61e, R.drawable.emoji_1f61e);\n        sEmojisMap.append(0x1f61f, R.drawable.emoji_1f61f);\n        sEmojisMap.append(0x1f620, R.drawable.emoji_1f620);\n        sEmojisMap.append(0x1f621, R.drawable.emoji_1f621);\n        sEmojisMap.append(0x1f622, R.drawable.emoji_1f622);\n        sEmojisMap.append(0x1f623, R.drawable.emoji_1f623);\n        sEmojisMap.append(0x1f624, R.drawable.emoji_1f624);\n        sEmojisMap.append(0x1f625, R.drawable.emoji_1f625);\n        sEmojisMap.append(0x1f626, R.drawable.emoji_1f626);\n        sEmojisMap.append(0x1f627, R.drawable.emoji_1f627);\n        sEmojisMap.append(0x1f628, R.drawable.emoji_1f628);\n        sEmojisMap.append(0x1f629, R.drawable.emoji_1f629);\n        sEmojisMap.append(0x1f62a, R.drawable.emoji_1f62a);\n        sEmojisMap.append(0x1f62b, R.drawable.emoji_1f62b);\n        sEmojisMap.append(0x1f62c, R.drawable.emoji_1f62c);\n        sEmojisMap.append(0x1f62d, R.drawable.emoji_1f62d);\n        sEmojisMap.append(0x1f62e, R.drawable.emoji_1f62e);\n        sEmojisMap.append(0x1f62f, R.drawable.emoji_1f62f);\n        sEmojisMap.append(0x1f630, R.drawable.emoji_1f630);\n        sEmojisMap.append(0x1f631, R.drawable.emoji_1f631);\n        sEmojisMap.append(0x1f632, R.drawable.emoji_1f632);\n        sEmojisMap.append(0x1f633, R.drawable.emoji_1f633);\n        sEmojisMap.append(0x1f634, R.drawable.emoji_1f634);\n        sEmojisMap.append(0x1f635, R.drawable.emoji_1f635);\n        sEmojisMap.append(0x1f636, R.drawable.emoji_1f636);\n        sEmojisMap.append(0x1f637, R.drawable.emoji_1f637);\n        sEmojisMap.append(0x1f638, R.drawable.emoji_1f638);\n        sEmojisMap.append(0x1f639, R.drawable.emoji_1f639);\n        sEmojisMap.append(0x1f63a, R.drawable.emoji_1f63a);\n        sEmojisMap.append(0x1f63b, R.drawable.emoji_1f63b);\n        sEmojisMap.append(0x1f63c, R.drawable.emoji_1f63c);\n        sEmojisMap.append(0x1f63d, R.drawable.emoji_1f63d);\n        sEmojisMap.append(0x1f63e, R.drawable.emoji_1f63e);\n        sEmojisMap.append(0x1f63f, R.drawable.emoji_1f63f);\n        sEmojisMap.append(0x1f640, R.drawable.emoji_1f640);\n        sEmojisMap.append(0x1f645, R.drawable.emoji_1f645);\n        sEmojisMap.append(0x1f646, R.drawable.emoji_1f646);\n        sEmojisMap.append(0x1f647, R.drawable.emoji_1f647);\n        sEmojisMap.append(0x1f648, R.drawable.emoji_1f648);\n        sEmojisMap.append(0x1f649, R.drawable.emoji_1f649);\n        sEmojisMap.append(0x1f64a, R.drawable.emoji_1f64a);\n        sEmojisMap.append(0x1f64b, R.drawable.emoji_1f64b);\n        sEmojisMap.append(0x1f64c, R.drawable.emoji_1f64c);\n        sEmojisMap.append(0x1f64d, R.drawable.emoji_1f64d);\n        sEmojisMap.append(0x1f64e, R.drawable.emoji_1f64e);\n        sEmojisMap.append(0x1f64f, R.drawable.emoji_1f64f);\n        sEmojisMap.append(0x1f680, R.drawable.emoji_1f680);\n        sEmojisMap.append(0x1f681, R.drawable.emoji_1f681);\n        sEmojisMap.append(0x1f682, R.drawable.emoji_1f682);\n        sEmojisMap.append(0x1f683, R.drawable.emoji_1f683);\n        sEmojisMap.append(0x1f684, R.drawable.emoji_1f684);\n        sEmojisMap.append(0x1f685, R.drawable.emoji_1f685);\n        sEmojisMap.append(0x1f686, R.drawable.emoji_1f686);\n        sEmojisMap.append(0x1f687, R.drawable.emoji_1f687);\n        sEmojisMap.append(0x1f688, R.drawable.emoji_1f688);\n        sEmojisMap.append(0x1f689, R.drawable.emoji_1f689);\n        sEmojisMap.append(0x1f68a, R.drawable.emoji_1f68a);\n        sEmojisMap.append(0x1f68b, R.drawable.emoji_1f68b);\n        sEmojisMap.append(0x1f68c, R.drawable.emoji_1f68c);\n        sEmojisMap.append(0x1f68d, R.drawable.emoji_1f68d);\n        sEmojisMap.append(0x1f68e, R.drawable.emoji_1f68e);\n        sEmojisMap.append(0x1f68f, R.drawable.emoji_1f68f);\n        sEmojisMap.append(0x1f690, R.drawable.emoji_1f690);\n        sEmojisMap.append(0x1f691, R.drawable.emoji_1f691);\n        sEmojisMap.append(0x1f692, R.drawable.emoji_1f692);\n        sEmojisMap.append(0x1f693, R.drawable.emoji_1f693);\n        sEmojisMap.append(0x1f694, R.drawable.emoji_1f694);\n        sEmojisMap.append(0x1f695, R.drawable.emoji_1f695);\n        sEmojisMap.append(0x1f696, R.drawable.emoji_1f696);\n        sEmojisMap.append(0x1f697, R.drawable.emoji_1f697);\n        sEmojisMap.append(0x1f698, R.drawable.emoji_1f698);\n        sEmojisMap.append(0x1f699, R.drawable.emoji_1f699);\n        sEmojisMap.append(0x1f69a, R.drawable.emoji_1f69a);\n        sEmojisMap.append(0x1f69b, R.drawable.emoji_1f69b);\n        sEmojisMap.append(0x1f69c, R.drawable.emoji_1f69c);\n        sEmojisMap.append(0x1f69d, R.drawable.emoji_1f69d);\n        sEmojisMap.append(0x1f69e, R.drawable.emoji_1f69e);\n        sEmojisMap.append(0x1f69f, R.drawable.emoji_1f69f);\n        sEmojisMap.append(0x1f6a0, R.drawable.emoji_1f6a0);\n        sEmojisMap.append(0x1f6a1, R.drawable.emoji_1f6a1);\n        sEmojisMap.append(0x1f6a2, R.drawable.emoji_1f6a2);\n        sEmojisMap.append(0x1f6a3, R.drawable.emoji_1f6a3);\n        sEmojisMap.append(0x1f6a4, R.drawable.emoji_1f6a4);\n        sEmojisMap.append(0x1f6a5, R.drawable.emoji_1f6a5);\n        sEmojisMap.append(0x1f6a6, R.drawable.emoji_1f6a6);\n        sEmojisMap.append(0x1f6a7, R.drawable.emoji_1f6a7);\n        sEmojisMap.append(0x1f6a8, R.drawable.emoji_1f6a8);\n        sEmojisMap.append(0x1f6a9, R.drawable.emoji_1f6a9);\n        sEmojisMap.append(0x1f6aa, R.drawable.emoji_1f6aa);\n        sEmojisMap.append(0x1f6ab, R.drawable.emoji_1f6ab);\n        sEmojisMap.append(0x1f6ac, R.drawable.emoji_1f6ac);\n        sEmojisMap.append(0x1f6ad, R.drawable.emoji_1f6ad);\n        sEmojisMap.append(0x1f6ae, R.drawable.emoji_1f6ae);\n        sEmojisMap.append(0x1f6af, R.drawable.emoji_1f6af);\n        sEmojisMap.append(0x1f6b0, R.drawable.emoji_1f6b0);\n        sEmojisMap.append(0x1f6b1, R.drawable.emoji_1f6b1);\n        sEmojisMap.append(0x1f6b2, R.drawable.emoji_1f6b2);\n        sEmojisMap.append(0x1f6b3, R.drawable.emoji_1f6b3);\n        sEmojisMap.append(0x1f6b4, R.drawable.emoji_1f6b4);\n        sEmojisMap.append(0x1f6b5, R.drawable.emoji_1f6b5);\n        sEmojisMap.append(0x1f6b6, R.drawable.emoji_1f6b6);\n        sEmojisMap.append(0x1f6b7, R.drawable.emoji_1f6b7);\n        sEmojisMap.append(0x1f6b8, R.drawable.emoji_1f6b8);\n        sEmojisMap.append(0x1f6b9, R.drawable.emoji_1f6b9);\n        sEmojisMap.append(0x1f6ba, R.drawable.emoji_1f6ba);\n        sEmojisMap.append(0x1f6bb, R.drawable.emoji_1f6bb);\n        sEmojisMap.append(0x1f6bc, R.drawable.emoji_1f6bc);\n        sEmojisMap.append(0x1f6bd, R.drawable.emoji_1f6bd);\n        sEmojisMap.append(0x1f6be, R.drawable.emoji_1f6be);\n        sEmojisMap.append(0x1f6bf, R.drawable.emoji_1f6bf);\n        sEmojisMap.append(0x1f6c0, R.drawable.emoji_1f6c0);\n        sEmojisMap.append(0x1f6c1, R.drawable.emoji_1f6c1);\n        sEmojisMap.append(0x1f6c2, R.drawable.emoji_1f6c2);\n        sEmojisMap.append(0x1f6c3, R.drawable.emoji_1f6c3);\n        sEmojisMap.append(0x1f6c4, R.drawable.emoji_1f6c4);\n        sEmojisMap.append(0x1f6c5, R.drawable.emoji_1f6c5);\n\n\n        sSoftbanksMap.append(0xe001, R.drawable.emoji_1f466);\n        sSoftbanksMap.append(0xe002, R.drawable.emoji_1f467);\n        sSoftbanksMap.append(0xe003, R.drawable.emoji_1f48b);\n        sSoftbanksMap.append(0xe004, R.drawable.emoji_1f468);\n        sSoftbanksMap.append(0xe005, R.drawable.emoji_1f469);\n        sSoftbanksMap.append(0xe006, R.drawable.emoji_1f455);\n        sSoftbanksMap.append(0xe007, R.drawable.emoji_1f45e);\n        sSoftbanksMap.append(0xe008, R.drawable.emoji_1f4f7);\n        sSoftbanksMap.append(0xe009, R.drawable.emoji_1f4de);\n        sSoftbanksMap.append(0xe00a, R.drawable.emoji_1f4f1);\n        sSoftbanksMap.append(0xe00b, R.drawable.emoji_1f4e0);\n        sSoftbanksMap.append(0xe00c, R.drawable.emoji_1f4bb);\n        sSoftbanksMap.append(0xe00d, R.drawable.emoji_1f44a);\n        sSoftbanksMap.append(0xe00e, R.drawable.emoji_1f44d);\n        sSoftbanksMap.append(0xe00f, R.drawable.emoji_261d);\n        sSoftbanksMap.append(0xe010, R.drawable.emoji_270a);\n        sSoftbanksMap.append(0xe011, R.drawable.emoji_270c);\n        sSoftbanksMap.append(0xe012, R.drawable.emoji_1f64b);\n        sSoftbanksMap.append(0xe013, R.drawable.emoji_1f3bf);\n        sSoftbanksMap.append(0xe014, R.drawable.emoji_26f3);\n        sSoftbanksMap.append(0xe015, R.drawable.emoji_1f3be);\n        sSoftbanksMap.append(0xe016, R.drawable.emoji_26be);\n        sSoftbanksMap.append(0xe017, R.drawable.emoji_1f3c4);\n        sSoftbanksMap.append(0xe018, R.drawable.emoji_26bd);\n        sSoftbanksMap.append(0xe019, R.drawable.emoji_1f3a3);\n        sSoftbanksMap.append(0xe01a, R.drawable.emoji_1f434);\n        sSoftbanksMap.append(0xe01b, R.drawable.emoji_1f697);\n        sSoftbanksMap.append(0xe01c, R.drawable.emoji_26f5);\n        sSoftbanksMap.append(0xe01d, R.drawable.emoji_2708);\n        sSoftbanksMap.append(0xe01e, R.drawable.emoji_1f683);\n        sSoftbanksMap.append(0xe01f, R.drawable.emoji_1f685);\n        sSoftbanksMap.append(0xe020, R.drawable.emoji_2753);\n        sSoftbanksMap.append(0xe021, R.drawable.emoji_2757);\n        sSoftbanksMap.append(0xe022, R.drawable.emoji_2764);\n        sSoftbanksMap.append(0xe023, R.drawable.emoji_1f494);\n        sSoftbanksMap.append(0xe024, R.drawable.emoji_1f550);\n        sSoftbanksMap.append(0xe025, R.drawable.emoji_1f551);\n        sSoftbanksMap.append(0xe026, R.drawable.emoji_1f552);\n        sSoftbanksMap.append(0xe027, R.drawable.emoji_1f553);\n        sSoftbanksMap.append(0xe028, R.drawable.emoji_1f554);\n        sSoftbanksMap.append(0xe029, R.drawable.emoji_1f555);\n        sSoftbanksMap.append(0xe02a, R.drawable.emoji_1f556);\n        sSoftbanksMap.append(0xe02b, R.drawable.emoji_1f557);\n        sSoftbanksMap.append(0xe02c, R.drawable.emoji_1f558);\n        sSoftbanksMap.append(0xe02d, R.drawable.emoji_1f559);\n        sSoftbanksMap.append(0xe02e, R.drawable.emoji_1f55a);\n        sSoftbanksMap.append(0xe02f, R.drawable.emoji_1f55b);\n        sSoftbanksMap.append(0xe030, R.drawable.emoji_1f338);\n        sSoftbanksMap.append(0xe031, R.drawable.emoji_1f531);\n        sSoftbanksMap.append(0xe032, R.drawable.emoji_1f339);\n        sSoftbanksMap.append(0xe033, R.drawable.emoji_1f384);\n        sSoftbanksMap.append(0xe034, R.drawable.emoji_1f48d);\n        sSoftbanksMap.append(0xe035, R.drawable.emoji_1f48e);\n        sSoftbanksMap.append(0xe036, R.drawable.emoji_1f3e0);\n        sSoftbanksMap.append(0xe037, R.drawable.emoji_26ea);\n        sSoftbanksMap.append(0xe038, R.drawable.emoji_1f3e2);\n        sSoftbanksMap.append(0xe039, R.drawable.emoji_1f689);\n        sSoftbanksMap.append(0xe03a, R.drawable.emoji_26fd);\n        sSoftbanksMap.append(0xe03b, R.drawable.emoji_1f5fb);\n        sSoftbanksMap.append(0xe03c, R.drawable.emoji_1f3a4);\n        sSoftbanksMap.append(0xe03d, R.drawable.emoji_1f3a5);\n        sSoftbanksMap.append(0xe03e, R.drawable.emoji_1f3b5);\n        sSoftbanksMap.append(0xe03f, R.drawable.emoji_1f511);\n        sSoftbanksMap.append(0xe040, R.drawable.emoji_1f3b7);\n        sSoftbanksMap.append(0xe041, R.drawable.emoji_1f3b8);\n        sSoftbanksMap.append(0xe042, R.drawable.emoji_1f3ba);\n        sSoftbanksMap.append(0xe043, R.drawable.emoji_1f374);\n        sSoftbanksMap.append(0xe044, R.drawable.emoji_1f377);\n        sSoftbanksMap.append(0xe045, R.drawable.emoji_2615);\n        sSoftbanksMap.append(0xe046, R.drawable.emoji_1f370);\n        sSoftbanksMap.append(0xe047, R.drawable.emoji_1f37a);\n        sSoftbanksMap.append(0xe048, R.drawable.emoji_26c4);\n        sSoftbanksMap.append(0xe049, R.drawable.emoji_2601);\n        sSoftbanksMap.append(0xe04a, R.drawable.emoji_2600);\n        sSoftbanksMap.append(0xe04b, R.drawable.emoji_2614);\n        sSoftbanksMap.append(0xe04c, R.drawable.emoji_1f313);\n        sSoftbanksMap.append(0xe04d, R.drawable.emoji_1f304);\n        sSoftbanksMap.append(0xe04e, R.drawable.emoji_1f47c);\n        sSoftbanksMap.append(0xe04f, R.drawable.emoji_1f431);\n        sSoftbanksMap.append(0xe050, R.drawable.emoji_1f42f);\n        sSoftbanksMap.append(0xe051, R.drawable.emoji_1f43b);\n        sSoftbanksMap.append(0xe052, R.drawable.emoji_1f429);\n        sSoftbanksMap.append(0xe053, R.drawable.emoji_1f42d);\n        sSoftbanksMap.append(0xe054, R.drawable.emoji_1f433);\n        sSoftbanksMap.append(0xe055, R.drawable.emoji_1f427);\n        sSoftbanksMap.append(0xe056, R.drawable.emoji_1f60a);\n        sSoftbanksMap.append(0xe057, R.drawable.emoji_1f603);\n        sSoftbanksMap.append(0xe058, R.drawable.emoji_1f61e);\n        sSoftbanksMap.append(0xe059, R.drawable.emoji_1f620);\n        sSoftbanksMap.append(0xe05a, R.drawable.emoji_1f4a9);\n        sSoftbanksMap.append(0xe101, R.drawable.emoji_1f4ea);\n        sSoftbanksMap.append(0xe102, R.drawable.emoji_1f4ee);\n        sSoftbanksMap.append(0xe103, R.drawable.emoji_1f4e7);\n        sSoftbanksMap.append(0xe104, R.drawable.emoji_1f4f2);\n        sSoftbanksMap.append(0xe105, R.drawable.emoji_1f61c);\n        sSoftbanksMap.append(0xe106, R.drawable.emoji_1f60d);\n        sSoftbanksMap.append(0xe107, R.drawable.emoji_1f631);\n        sSoftbanksMap.append(0xe108, R.drawable.emoji_1f613);\n        sSoftbanksMap.append(0xe109, R.drawable.emoji_1f435);\n        sSoftbanksMap.append(0xe10a, R.drawable.emoji_1f419);\n        sSoftbanksMap.append(0xe10b, R.drawable.emoji_1f437);\n        sSoftbanksMap.append(0xe10c, R.drawable.emoji_1f47d);\n        sSoftbanksMap.append(0xe10d, R.drawable.emoji_1f680);\n        sSoftbanksMap.append(0xe10e, R.drawable.emoji_1f451);\n        sSoftbanksMap.append(0xe10f, R.drawable.emoji_1f4a1);\n        sSoftbanksMap.append(0xe110, R.drawable.emoji_1f331);\n        sSoftbanksMap.append(0xe111, R.drawable.emoji_1f48f);\n        sSoftbanksMap.append(0xe112, R.drawable.emoji_1f381);\n        sSoftbanksMap.append(0xe113, R.drawable.emoji_1f52b);\n        sSoftbanksMap.append(0xe114, R.drawable.emoji_1f50d);\n        sSoftbanksMap.append(0xe115, R.drawable.emoji_1f3c3);\n        sSoftbanksMap.append(0xe116, R.drawable.emoji_1f528);\n        sSoftbanksMap.append(0xe117, R.drawable.emoji_1f386);\n        sSoftbanksMap.append(0xe118, R.drawable.emoji_1f341);\n        sSoftbanksMap.append(0xe119, R.drawable.emoji_1f342);\n        sSoftbanksMap.append(0xe11a, R.drawable.emoji_1f47f);\n        sSoftbanksMap.append(0xe11b, R.drawable.emoji_1f47b);\n        sSoftbanksMap.append(0xe11c, R.drawable.emoji_1f480);\n        sSoftbanksMap.append(0xe11d, R.drawable.emoji_1f525);\n        sSoftbanksMap.append(0xe11e, R.drawable.emoji_1f4bc);\n        sSoftbanksMap.append(0xe11f, R.drawable.emoji_1f4ba);\n        sSoftbanksMap.append(0xe120, R.drawable.emoji_1f354);\n        sSoftbanksMap.append(0xe121, R.drawable.emoji_26f2);\n        sSoftbanksMap.append(0xe122, R.drawable.emoji_26fa);\n        sSoftbanksMap.append(0xe123, R.drawable.emoji_2668);\n        sSoftbanksMap.append(0xe124, R.drawable.emoji_1f3a1);\n        sSoftbanksMap.append(0xe125, R.drawable.emoji_1f3ab);\n        sSoftbanksMap.append(0xe126, R.drawable.emoji_1f4bf);\n        sSoftbanksMap.append(0xe127, R.drawable.emoji_1f4c0);\n        sSoftbanksMap.append(0xe128, R.drawable.emoji_1f4fb);\n        sSoftbanksMap.append(0xe129, R.drawable.emoji_1f4fc);\n        sSoftbanksMap.append(0xe12a, R.drawable.emoji_1f4fa);\n        sSoftbanksMap.append(0xe12b, R.drawable.emoji_1f47e);\n        sSoftbanksMap.append(0xe12c, R.drawable.emoji_303d);\n        sSoftbanksMap.append(0xe12d, R.drawable.emoji_1f004);\n        sSoftbanksMap.append(0xe12e, R.drawable.emoji_1f19a);\n        sSoftbanksMap.append(0xe12f, R.drawable.emoji_1f4b0);\n        sSoftbanksMap.append(0xe130, R.drawable.emoji_1f3af);\n        sSoftbanksMap.append(0xe131, R.drawable.emoji_1f3c6);\n        sSoftbanksMap.append(0xe132, R.drawable.emoji_1f3c1);\n        sSoftbanksMap.append(0xe133, R.drawable.emoji_1f3b0);\n        sSoftbanksMap.append(0xe134, R.drawable.emoji_1f40e);\n        sSoftbanksMap.append(0xe135, R.drawable.emoji_1f6a4);\n        sSoftbanksMap.append(0xe136, R.drawable.emoji_1f6b2);\n        sSoftbanksMap.append(0xe137, R.drawable.emoji_1f6a7);\n        sSoftbanksMap.append(0xe138, R.drawable.emoji_1f6b9);\n        sSoftbanksMap.append(0xe139, R.drawable.emoji_1f6ba);\n        sSoftbanksMap.append(0xe13a, R.drawable.emoji_1f6bc);\n        sSoftbanksMap.append(0xe13b, R.drawable.emoji_1f489);\n        sSoftbanksMap.append(0xe13c, R.drawable.emoji_1f4a4);\n        sSoftbanksMap.append(0xe13d, R.drawable.emoji_26a1);\n        sSoftbanksMap.append(0xe13e, R.drawable.emoji_1f460);\n        sSoftbanksMap.append(0xe13f, R.drawable.emoji_1f6c0);\n        sSoftbanksMap.append(0xe140, R.drawable.emoji_1f6bd);\n        sSoftbanksMap.append(0xe141, R.drawable.emoji_1f50a);\n        sSoftbanksMap.append(0xe142, R.drawable.emoji_1f4e2);\n        sSoftbanksMap.append(0xe143, R.drawable.emoji_1f38c);\n        sSoftbanksMap.append(0xe144, R.drawable.emoji_1f50f);\n        sSoftbanksMap.append(0xe145, R.drawable.emoji_1f513);\n        sSoftbanksMap.append(0xe146, R.drawable.emoji_1f306);\n        sSoftbanksMap.append(0xe147, R.drawable.emoji_1f373);\n        sSoftbanksMap.append(0xe148, R.drawable.emoji_1f4c7);\n        sSoftbanksMap.append(0xe149, R.drawable.emoji_1f4b1);\n        sSoftbanksMap.append(0xe14a, R.drawable.emoji_1f4b9);\n        sSoftbanksMap.append(0xe14b, R.drawable.emoji_1f4e1);\n        sSoftbanksMap.append(0xe14c, R.drawable.emoji_1f4aa);\n        sSoftbanksMap.append(0xe14d, R.drawable.emoji_1f3e6);\n        sSoftbanksMap.append(0xe14e, R.drawable.emoji_1f6a5);\n        sSoftbanksMap.append(0xe14f, R.drawable.emoji_1f17f);\n        sSoftbanksMap.append(0xe150, R.drawable.emoji_1f68f);\n        sSoftbanksMap.append(0xe151, R.drawable.emoji_1f6bb);\n        sSoftbanksMap.append(0xe152, R.drawable.emoji_1f46e);\n        sSoftbanksMap.append(0xe153, R.drawable.emoji_1f3e3);\n        sSoftbanksMap.append(0xe154, R.drawable.emoji_1f3e7);\n        sSoftbanksMap.append(0xe155, R.drawable.emoji_1f3e5);\n        sSoftbanksMap.append(0xe156, R.drawable.emoji_1f3ea);\n        sSoftbanksMap.append(0xe157, R.drawable.emoji_1f3eb);\n        sSoftbanksMap.append(0xe158, R.drawable.emoji_1f3e8);\n        sSoftbanksMap.append(0xe159, R.drawable.emoji_1f68c);\n        sSoftbanksMap.append(0xe15a, R.drawable.emoji_1f695);\n        sSoftbanksMap.append(0xe201, R.drawable.emoji_1f6b6);\n        sSoftbanksMap.append(0xe202, R.drawable.emoji_1f6a2);\n        sSoftbanksMap.append(0xe203, R.drawable.emoji_1f201);\n        sSoftbanksMap.append(0xe204, R.drawable.emoji_1f49f);\n        sSoftbanksMap.append(0xe205, R.drawable.emoji_2734);\n        sSoftbanksMap.append(0xe206, R.drawable.emoji_2733);\n        sSoftbanksMap.append(0xe207, R.drawable.emoji_1f51e);\n        sSoftbanksMap.append(0xe208, R.drawable.emoji_1f6ad);\n        sSoftbanksMap.append(0xe209, R.drawable.emoji_1f530);\n        sSoftbanksMap.append(0xe20a, R.drawable.emoji_267f);\n        sSoftbanksMap.append(0xe20b, R.drawable.emoji_1f4f6);\n        sSoftbanksMap.append(0xe20c, R.drawable.emoji_2665);\n        sSoftbanksMap.append(0xe20d, R.drawable.emoji_2666);\n        sSoftbanksMap.append(0xe20e, R.drawable.emoji_2660);\n        sSoftbanksMap.append(0xe20f, R.drawable.emoji_2663);\n        sSoftbanksMap.append(0xe210, R.drawable.emoji_0023);\n        sSoftbanksMap.append(0xe211, R.drawable.emoji_27bf);\n        sSoftbanksMap.append(0xe212, R.drawable.emoji_1f195);\n        sSoftbanksMap.append(0xe213, R.drawable.emoji_1f199);\n        sSoftbanksMap.append(0xe214, R.drawable.emoji_1f192);\n        sSoftbanksMap.append(0xe215, R.drawable.emoji_1f236);\n        sSoftbanksMap.append(0xe216, R.drawable.emoji_1f21a);\n        sSoftbanksMap.append(0xe217, R.drawable.emoji_1f237);\n        sSoftbanksMap.append(0xe218, R.drawable.emoji_1f238);\n        sSoftbanksMap.append(0xe219, R.drawable.emoji_1f534);\n        sSoftbanksMap.append(0xe21a, R.drawable.emoji_1f532);\n        sSoftbanksMap.append(0xe21b, R.drawable.emoji_1f533);\n        sSoftbanksMap.append(0xe21c, R.drawable.emoji_0031);\n        sSoftbanksMap.append(0xe21d, R.drawable.emoji_0032);\n        sSoftbanksMap.append(0xe21e, R.drawable.emoji_0033);\n        sSoftbanksMap.append(0xe21f, R.drawable.emoji_0034);\n        sSoftbanksMap.append(0xe220, R.drawable.emoji_0035);\n        sSoftbanksMap.append(0xe221, R.drawable.emoji_0036);\n        sSoftbanksMap.append(0xe222, R.drawable.emoji_0037);\n        sSoftbanksMap.append(0xe223, R.drawable.emoji_0038);\n        sSoftbanksMap.append(0xe224, R.drawable.emoji_0039);\n        sSoftbanksMap.append(0xe225, R.drawable.emoji_0030);\n        sSoftbanksMap.append(0xe226, R.drawable.emoji_1f250);\n        sSoftbanksMap.append(0xe227, R.drawable.emoji_1f239);\n        sSoftbanksMap.append(0xe228, R.drawable.emoji_1f202);\n        sSoftbanksMap.append(0xe229, R.drawable.emoji_1f194);\n        sSoftbanksMap.append(0xe22a, R.drawable.emoji_1f235);\n        sSoftbanksMap.append(0xe22b, R.drawable.emoji_1f233);\n        sSoftbanksMap.append(0xe22c, R.drawable.emoji_1f22f);\n        sSoftbanksMap.append(0xe22d, R.drawable.emoji_1f23a);\n        sSoftbanksMap.append(0xe22e, R.drawable.emoji_1f446);\n        sSoftbanksMap.append(0xe22f, R.drawable.emoji_1f447);\n        sSoftbanksMap.append(0xe230, R.drawable.emoji_1f448);\n        sSoftbanksMap.append(0xe231, R.drawable.emoji_1f449);\n        sSoftbanksMap.append(0xe232, R.drawable.emoji_2b06);\n        sSoftbanksMap.append(0xe233, R.drawable.emoji_2b07);\n        sSoftbanksMap.append(0xe234, R.drawable.emoji_27a1);\n        sSoftbanksMap.append(0xe235, R.drawable.emoji_1f519);\n        sSoftbanksMap.append(0xe236, R.drawable.emoji_2197);\n        sSoftbanksMap.append(0xe237, R.drawable.emoji_2196);\n        sSoftbanksMap.append(0xe238, R.drawable.emoji_2198);\n        sSoftbanksMap.append(0xe239, R.drawable.emoji_2199);\n        sSoftbanksMap.append(0xe23a, R.drawable.emoji_25b6);\n        sSoftbanksMap.append(0xe23b, R.drawable.emoji_25c0);\n        sSoftbanksMap.append(0xe23c, R.drawable.emoji_23e9);\n        sSoftbanksMap.append(0xe23d, R.drawable.emoji_23ea);\n        sSoftbanksMap.append(0xe23e, R.drawable.emoji_1f52e);\n        sSoftbanksMap.append(0xe23f, R.drawable.emoji_2648);\n        sSoftbanksMap.append(0xe240, R.drawable.emoji_2649);\n        sSoftbanksMap.append(0xe241, R.drawable.emoji_264a);\n        sSoftbanksMap.append(0xe242, R.drawable.emoji_264b);\n        sSoftbanksMap.append(0xe243, R.drawable.emoji_264c);\n        sSoftbanksMap.append(0xe244, R.drawable.emoji_264d);\n        sSoftbanksMap.append(0xe245, R.drawable.emoji_264e);\n        sSoftbanksMap.append(0xe246, R.drawable.emoji_264f);\n        sSoftbanksMap.append(0xe247, R.drawable.emoji_2650);\n        sSoftbanksMap.append(0xe248, R.drawable.emoji_2651);\n        sSoftbanksMap.append(0xe249, R.drawable.emoji_2652);\n        sSoftbanksMap.append(0xe24a, R.drawable.emoji_2653);\n        sSoftbanksMap.append(0xe24b, R.drawable.emoji_26ce);\n        sSoftbanksMap.append(0xe24c, R.drawable.emoji_1f51d);\n        sSoftbanksMap.append(0xe24d, R.drawable.emoji_1f197);\n        sSoftbanksMap.append(0xe24e, R.drawable.emoji_00a9);\n        sSoftbanksMap.append(0xe24f, R.drawable.emoji_00ae);\n        sSoftbanksMap.append(0xe250, R.drawable.emoji_1f4f3);\n        sSoftbanksMap.append(0xe251, R.drawable.emoji_1f4f4);\n        sSoftbanksMap.append(0xe252, R.drawable.emoji_26a0);\n        sSoftbanksMap.append(0xe253, R.drawable.emoji_1f481);\n        sSoftbanksMap.append(0xe301, R.drawable.emoji_1f4c3);\n        sSoftbanksMap.append(0xe302, R.drawable.emoji_1f454);\n        sSoftbanksMap.append(0xe303, R.drawable.emoji_1f33a);\n        sSoftbanksMap.append(0xe304, R.drawable.emoji_1f337);\n        sSoftbanksMap.append(0xe305, R.drawable.emoji_1f33b);\n        sSoftbanksMap.append(0xe306, R.drawable.emoji_1f490);\n        sSoftbanksMap.append(0xe307, R.drawable.emoji_1f334);\n        sSoftbanksMap.append(0xe308, R.drawable.emoji_1f335);\n        sSoftbanksMap.append(0xe309, R.drawable.emoji_1f6be);\n        sSoftbanksMap.append(0xe30a, R.drawable.emoji_1f3a7);\n        sSoftbanksMap.append(0xe30b, R.drawable.emoji_1f376);\n        sSoftbanksMap.append(0xe30c, R.drawable.emoji_1f37b);\n        sSoftbanksMap.append(0xe30d, R.drawable.emoji_3297);\n        sSoftbanksMap.append(0xe30e, R.drawable.emoji_1f6ac);\n        sSoftbanksMap.append(0xe30f, R.drawable.emoji_1f48a);\n        sSoftbanksMap.append(0xe310, R.drawable.emoji_1f388);\n        sSoftbanksMap.append(0xe311, R.drawable.emoji_1f4a3);\n        sSoftbanksMap.append(0xe312, R.drawable.emoji_1f389);\n        sSoftbanksMap.append(0xe313, R.drawable.emoji_2702);\n        sSoftbanksMap.append(0xe314, R.drawable.emoji_1f380);\n        sSoftbanksMap.append(0xe315, R.drawable.emoji_3299);\n        sSoftbanksMap.append(0xe316, R.drawable.emoji_1f4bd);\n        sSoftbanksMap.append(0xe317, R.drawable.emoji_1f4e3);\n        sSoftbanksMap.append(0xe318, R.drawable.emoji_1f452);\n        sSoftbanksMap.append(0xe319, R.drawable.emoji_1f457);\n        sSoftbanksMap.append(0xe31a, R.drawable.emoji_1f461);\n        sSoftbanksMap.append(0xe31b, R.drawable.emoji_1f462);\n        sSoftbanksMap.append(0xe31c, R.drawable.emoji_1f484);\n        sSoftbanksMap.append(0xe31d, R.drawable.emoji_1f485);\n        sSoftbanksMap.append(0xe31e, R.drawable.emoji_1f486);\n        sSoftbanksMap.append(0xe31f, R.drawable.emoji_1f487);\n        sSoftbanksMap.append(0xe320, R.drawable.emoji_1f488);\n        sSoftbanksMap.append(0xe321, R.drawable.emoji_1f458);\n        sSoftbanksMap.append(0xe322, R.drawable.emoji_1f459);\n        sSoftbanksMap.append(0xe323, R.drawable.emoji_1f45c);\n        sSoftbanksMap.append(0xe324, R.drawable.emoji_1f3ac);\n        sSoftbanksMap.append(0xe325, R.drawable.emoji_1f514);\n        sSoftbanksMap.append(0xe326, R.drawable.emoji_1f3b6);\n        sSoftbanksMap.append(0xe327, R.drawable.emoji_1f493);\n        sSoftbanksMap.append(0xe328, R.drawable.emoji_1f48c);\n        sSoftbanksMap.append(0xe329, R.drawable.emoji_1f498);\n        sSoftbanksMap.append(0xe32a, R.drawable.emoji_1f499);\n        sSoftbanksMap.append(0xe32b, R.drawable.emoji_1f49a);\n        sSoftbanksMap.append(0xe32c, R.drawable.emoji_1f49b);\n        sSoftbanksMap.append(0xe32d, R.drawable.emoji_1f49c);\n        sSoftbanksMap.append(0xe32e, R.drawable.emoji_2728);\n        sSoftbanksMap.append(0xe32f, R.drawable.emoji_2b50);\n        sSoftbanksMap.append(0xe330, R.drawable.emoji_1f4a8);\n        sSoftbanksMap.append(0xe331, R.drawable.emoji_1f4a6);\n        sSoftbanksMap.append(0xe332, R.drawable.emoji_2b55);\n        sSoftbanksMap.append(0xe333, R.drawable.emoji_2716);\n        sSoftbanksMap.append(0xe334, R.drawable.emoji_1f4a2);\n        sSoftbanksMap.append(0xe335, R.drawable.emoji_1f31f);\n        sSoftbanksMap.append(0xe336, R.drawable.emoji_2754);\n        sSoftbanksMap.append(0xe337, R.drawable.emoji_2755);\n        sSoftbanksMap.append(0xe338, R.drawable.emoji_1f375);\n        sSoftbanksMap.append(0xe339, R.drawable.emoji_1f35e);\n        sSoftbanksMap.append(0xe33a, R.drawable.emoji_1f366);\n        sSoftbanksMap.append(0xe33b, R.drawable.emoji_1f35f);\n        sSoftbanksMap.append(0xe33c, R.drawable.emoji_1f361);\n        sSoftbanksMap.append(0xe33d, R.drawable.emoji_1f358);\n        sSoftbanksMap.append(0xe33e, R.drawable.emoji_1f35a);\n        sSoftbanksMap.append(0xe33f, R.drawable.emoji_1f35d);\n        sSoftbanksMap.append(0xe340, R.drawable.emoji_1f35c);\n        sSoftbanksMap.append(0xe341, R.drawable.emoji_1f35b);\n        sSoftbanksMap.append(0xe342, R.drawable.emoji_1f359);\n        sSoftbanksMap.append(0xe343, R.drawable.emoji_1f362);\n        sSoftbanksMap.append(0xe344, R.drawable.emoji_1f363);\n        sSoftbanksMap.append(0xe345, R.drawable.emoji_1f34e);\n        sSoftbanksMap.append(0xe346, R.drawable.emoji_1f34a);\n        sSoftbanksMap.append(0xe347, R.drawable.emoji_1f353);\n        sSoftbanksMap.append(0xe348, R.drawable.emoji_1f349);\n        sSoftbanksMap.append(0xe349, R.drawable.emoji_1f345);\n        sSoftbanksMap.append(0xe34a, R.drawable.emoji_1f346);\n        sSoftbanksMap.append(0xe34b, R.drawable.emoji_1f382);\n        sSoftbanksMap.append(0xe34c, R.drawable.emoji_1f371);\n        sSoftbanksMap.append(0xe34d, R.drawable.emoji_1f372);\n        sSoftbanksMap.append(0xe401, R.drawable.emoji_1f625);\n        sSoftbanksMap.append(0xe402, R.drawable.emoji_1f60f);\n        sSoftbanksMap.append(0xe403, R.drawable.emoji_1f614);\n        sSoftbanksMap.append(0xe404, R.drawable.emoji_1f601);\n        sSoftbanksMap.append(0xe405, R.drawable.emoji_1f609);\n        sSoftbanksMap.append(0xe406, R.drawable.emoji_1f623);\n        sSoftbanksMap.append(0xe407, R.drawable.emoji_1f616);\n        sSoftbanksMap.append(0xe408, R.drawable.emoji_1f62a);\n        sSoftbanksMap.append(0xe409, R.drawable.emoji_1f445);\n        sSoftbanksMap.append(0xe40a, R.drawable.emoji_1f606);\n        sSoftbanksMap.append(0xe40b, R.drawable.emoji_1f628);\n        sSoftbanksMap.append(0xe40c, R.drawable.emoji_1f637);\n        sSoftbanksMap.append(0xe40d, R.drawable.emoji_1f633);\n        sSoftbanksMap.append(0xe40e, R.drawable.emoji_1f612);\n        sSoftbanksMap.append(0xe40f, R.drawable.emoji_1f630);\n        sSoftbanksMap.append(0xe410, R.drawable.emoji_1f632);\n        sSoftbanksMap.append(0xe411, R.drawable.emoji_1f62d);\n        sSoftbanksMap.append(0xe412, R.drawable.emoji_1f602);\n        sSoftbanksMap.append(0xe413, R.drawable.emoji_1f622);\n        sSoftbanksMap.append(0xe414, R.drawable.emoji_263a);\n        sSoftbanksMap.append(0xe415, R.drawable.emoji_1f605);\n        sSoftbanksMap.append(0xe416, R.drawable.emoji_1f621);\n        sSoftbanksMap.append(0xe417, R.drawable.emoji_1f61a);\n        sSoftbanksMap.append(0xe418, R.drawable.emoji_1f618);\n        sSoftbanksMap.append(0xe419, R.drawable.emoji_1f440);\n        sSoftbanksMap.append(0xe41a, R.drawable.emoji_1f443);\n        sSoftbanksMap.append(0xe41b, R.drawable.emoji_1f442);\n        sSoftbanksMap.append(0xe41c, R.drawable.emoji_1f444);\n        sSoftbanksMap.append(0xe41d, R.drawable.emoji_1f64f);\n        sSoftbanksMap.append(0xe41e, R.drawable.emoji_1f44b);\n        sSoftbanksMap.append(0xe41f, R.drawable.emoji_1f44f);\n        sSoftbanksMap.append(0xe420, R.drawable.emoji_1f44c);\n        sSoftbanksMap.append(0xe421, R.drawable.emoji_1f44e);\n        sSoftbanksMap.append(0xe422, R.drawable.emoji_1f450);\n        sSoftbanksMap.append(0xe423, R.drawable.emoji_1f645);\n        sSoftbanksMap.append(0xe424, R.drawable.emoji_1f646);\n        sSoftbanksMap.append(0xe425, R.drawable.emoji_1f491);\n        sSoftbanksMap.append(0xe426, R.drawable.emoji_1f647);\n        sSoftbanksMap.append(0xe427, R.drawable.emoji_1f64c);\n        sSoftbanksMap.append(0xe428, R.drawable.emoji_1f46b);\n        sSoftbanksMap.append(0xe429, R.drawable.emoji_1f46f);\n        sSoftbanksMap.append(0xe42a, R.drawable.emoji_1f3c0);\n        sSoftbanksMap.append(0xe42b, R.drawable.emoji_1f3c8);\n        sSoftbanksMap.append(0xe42c, R.drawable.emoji_1f3b1);\n        sSoftbanksMap.append(0xe42d, R.drawable.emoji_1f3ca);\n        sSoftbanksMap.append(0xe42e, R.drawable.emoji_1f699);\n        sSoftbanksMap.append(0xe42f, R.drawable.emoji_1f69a);\n        sSoftbanksMap.append(0xe430, R.drawable.emoji_1f692);\n        sSoftbanksMap.append(0xe431, R.drawable.emoji_1f691);\n        sSoftbanksMap.append(0xe432, R.drawable.emoji_1f693);\n        sSoftbanksMap.append(0xe433, R.drawable.emoji_1f3a2);\n        sSoftbanksMap.append(0xe434, R.drawable.emoji_1f687);\n        sSoftbanksMap.append(0xe435, R.drawable.emoji_1f684);\n        sSoftbanksMap.append(0xe436, R.drawable.emoji_1f38d);\n        sSoftbanksMap.append(0xe437, R.drawable.emoji_1f49d);\n        sSoftbanksMap.append(0xe438, R.drawable.emoji_1f38e);\n        sSoftbanksMap.append(0xe439, R.drawable.emoji_1f393);\n        sSoftbanksMap.append(0xe43a, R.drawable.emoji_1f392);\n        sSoftbanksMap.append(0xe43b, R.drawable.emoji_1f38f);\n        sSoftbanksMap.append(0xe43c, R.drawable.emoji_1f302);\n        sSoftbanksMap.append(0xe43d, R.drawable.emoji_1f492);\n        sSoftbanksMap.append(0xe43e, R.drawable.emoji_1f30a);\n        sSoftbanksMap.append(0xe43f, R.drawable.emoji_1f367);\n        sSoftbanksMap.append(0xe440, R.drawable.emoji_1f387);\n        sSoftbanksMap.append(0xe441, R.drawable.emoji_1f41a);\n        sSoftbanksMap.append(0xe442, R.drawable.emoji_1f390);\n        sSoftbanksMap.append(0xe443, R.drawable.emoji_1f300);\n        sSoftbanksMap.append(0xe444, R.drawable.emoji_1f33e);\n        sSoftbanksMap.append(0xe445, R.drawable.emoji_1f383);\n        sSoftbanksMap.append(0xe446, R.drawable.emoji_1f391);\n        sSoftbanksMap.append(0xe447, R.drawable.emoji_1f343);\n        sSoftbanksMap.append(0xe448, R.drawable.emoji_1f385);\n        sSoftbanksMap.append(0xe449, R.drawable.emoji_1f305);\n        sSoftbanksMap.append(0xe44a, R.drawable.emoji_1f307);\n        sSoftbanksMap.append(0xe44b, R.drawable.emoji_1f303);\n        sSoftbanksMap.append(0xe44b, R.drawable.emoji_1f30c);\n        sSoftbanksMap.append(0xe44c, R.drawable.emoji_1f308);\n        sSoftbanksMap.append(0xe501, R.drawable.emoji_1f3e9);\n        sSoftbanksMap.append(0xe502, R.drawable.emoji_1f3a8);\n        sSoftbanksMap.append(0xe503, R.drawable.emoji_1f3a9);\n        sSoftbanksMap.append(0xe504, R.drawable.emoji_1f3ec);\n        sSoftbanksMap.append(0xe505, R.drawable.emoji_1f3ef);\n        sSoftbanksMap.append(0xe506, R.drawable.emoji_1f3f0);\n        sSoftbanksMap.append(0xe507, R.drawable.emoji_1f3a6);\n        sSoftbanksMap.append(0xe508, R.drawable.emoji_1f3ed);\n        sSoftbanksMap.append(0xe509, R.drawable.emoji_1f5fc);\n        sSoftbanksMap.append(0xe50b, R.drawable.emoji_1f1ef_1f1f5);\n        sSoftbanksMap.append(0xe50c, R.drawable.emoji_1f1fa_1f1f8);\n        sSoftbanksMap.append(0xe50d, R.drawable.emoji_1f1eb_1f1f7);\n        sSoftbanksMap.append(0xe50e, R.drawable.emoji_1f1e9_1f1ea);\n        sSoftbanksMap.append(0xe50f, R.drawable.emoji_1f1ee_1f1f9);\n        sSoftbanksMap.append(0xe510, R.drawable.emoji_1f1ec_1f1e7);\n        sSoftbanksMap.append(0xe511, R.drawable.emoji_1f1ea_1f1f8);\n        sSoftbanksMap.append(0xe512, R.drawable.emoji_1f1f7_1f1fa);\n        sSoftbanksMap.append(0xe513, R.drawable.emoji_1f1e8_1f1f3);\n        sSoftbanksMap.append(0xe514, R.drawable.emoji_1f1f0_1f1f7);\n        sSoftbanksMap.append(0xe515, R.drawable.emoji_1f471);\n        sSoftbanksMap.append(0xe516, R.drawable.emoji_1f472);\n        sSoftbanksMap.append(0xe517, R.drawable.emoji_1f473);\n        sSoftbanksMap.append(0xe518, R.drawable.emoji_1f474);\n        sSoftbanksMap.append(0xe519, R.drawable.emoji_1f475);\n        sSoftbanksMap.append(0xe51a, R.drawable.emoji_1f476);\n        sSoftbanksMap.append(0xe51b, R.drawable.emoji_1f477);\n        sSoftbanksMap.append(0xe51c, R.drawable.emoji_1f478);\n        sSoftbanksMap.append(0xe51d, R.drawable.emoji_1f5fd);\n        sSoftbanksMap.append(0xe51e, R.drawable.emoji_1f482);\n        sSoftbanksMap.append(0xe51f, R.drawable.emoji_1f483);\n        sSoftbanksMap.append(0xe520, R.drawable.emoji_1f42c);\n        sSoftbanksMap.append(0xe521, R.drawable.emoji_1f426);\n        sSoftbanksMap.append(0xe522, R.drawable.emoji_1f420);\n        sSoftbanksMap.append(0xe523, R.drawable.emoji_1f423);\n        sSoftbanksMap.append(0xe524, R.drawable.emoji_1f439);\n        sSoftbanksMap.append(0xe525, R.drawable.emoji_1f41b);\n        sSoftbanksMap.append(0xe526, R.drawable.emoji_1f418);\n        sSoftbanksMap.append(0xe527, R.drawable.emoji_1f428);\n        sSoftbanksMap.append(0xe528, R.drawable.emoji_1f412);\n        sSoftbanksMap.append(0xe529, R.drawable.emoji_1f411);\n        sSoftbanksMap.append(0xe52a, R.drawable.emoji_1f43a);\n        sSoftbanksMap.append(0xe52b, R.drawable.emoji_1f42e);\n        sSoftbanksMap.append(0xe52c, R.drawable.emoji_1f430);\n        sSoftbanksMap.append(0xe52d, R.drawable.emoji_1f40d);\n        sSoftbanksMap.append(0xe52e, R.drawable.emoji_1f414);\n        sSoftbanksMap.append(0xe52f, R.drawable.emoji_1f417);\n        sSoftbanksMap.append(0xe530, R.drawable.emoji_1f42b);\n        sSoftbanksMap.append(0xe531, R.drawable.emoji_1f438);\n        sSoftbanksMap.append(0xe532, R.drawable.emoji_1f170);\n        sSoftbanksMap.append(0xe533, R.drawable.emoji_1f171);\n        sSoftbanksMap.append(0xe534, R.drawable.emoji_1f18e);\n        sSoftbanksMap.append(0xe535, R.drawable.emoji_1f17e);\n        sSoftbanksMap.append(0xe536, R.drawable.emoji_1f43e);\n        sSoftbanksMap.append(0xe537, R.drawable.emoji_2122);\n\n        Log.d(\"emoji\", String.format(\"init emoji cost: %dms\", (System.currentTimeMillis() - start)));\n    }\n\n    public static QDQQFaceManager getInstance() {\n        return sQDQQFaceManager;\n    }\n\n    @Override\n    public Drawable getSpecialBoundsDrawable(CharSequence text) {\n        return null;\n    }\n\n    @Override\n    public int getSpecialDrawableMaxHeight() {\n        return 0;\n    }\n\n    @Override\n    public boolean maybeSoftBankEmoji(char c) {\n        return ((c >> 12) == 0xe);\n    }\n\n    @Override\n    public boolean maybeEmoji(int codePoint) {\n        return codePoint > 0xff;\n    }\n\n    @Override\n    public int getEmojiResource(int codePoint) {\n        return sEmojisMap.get(codePoint);\n    }\n\n    @Override\n    public int getSoftbankEmojiResource(char c) {\n        return sSoftbanksMap.get(c);\n    }\n\n    @Override\n    public int getDoubleUnicodeEmoji(int currentCodePoint, int nextCodePoint) {\n        int icon = 0;\n        if (nextCodePoint == 0x20e3) {\n            switch (currentCodePoint) {\n                case 0x0031:\n                    icon = R.drawable.emoji_0031;\n                    break;\n                case 0x0032:\n                    icon = R.drawable.emoji_0032;\n                    break;\n                case 0x0033:\n                    icon = R.drawable.emoji_0033;\n                    break;\n                case 0x0034:\n                    icon = R.drawable.emoji_0034;\n                    break;\n                case 0x0035:\n                    icon = R.drawable.emoji_0035;\n                    break;\n                case 0x0036:\n                    icon = R.drawable.emoji_0036;\n                    break;\n                case 0x0037:\n                    icon = R.drawable.emoji_0037;\n                    break;\n                case 0x0038:\n                    icon = R.drawable.emoji_0038;\n                    break;\n                case 0x0039:\n                    icon = R.drawable.emoji_0039;\n                    break;\n                case 0x0030:\n                    icon = R.drawable.emoji_0030;\n                    break;\n                case 0x0023:\n                    icon = R.drawable.emoji_0023;\n                    break;\n                default:\n                    break;\n            }\n        } else {\n            switch (currentCodePoint) {\n                case 0x1f1ef:\n                    icon = (nextCodePoint == 0x1f1f5) ? R.drawable.emoji_1f1ef_1f1f5 : 0;\n                    break;\n                case 0x1f1fa:\n                    icon = (nextCodePoint == 0x1f1f8) ? R.drawable.emoji_1f1fa_1f1f8 : 0;\n                    break;\n                case 0x1f1eb:\n                    icon = (nextCodePoint == 0x1f1f7) ? R.drawable.emoji_1f1eb_1f1f7 : 0;\n                    break;\n                case 0x1f1e9:\n                    icon = (nextCodePoint == 0x1f1ea) ? R.drawable.emoji_1f1e9_1f1ea : 0;\n                    break;\n                case 0x1f1ee:\n                    icon = (nextCodePoint == 0x1f1f9) ? R.drawable.emoji_1f1ee_1f1f9 : 0;\n                    break;\n                case 0x1f1ec:\n                    icon = (nextCodePoint == 0x1f1e7) ? R.drawable.emoji_1f1ec_1f1e7 : 0;\n                    break;\n                case 0x1f1ea:\n                    icon = (nextCodePoint == 0x1f1f8) ? R.drawable.emoji_1f1ea_1f1f8 : 0;\n                    break;\n                case 0x1f1f7:\n                    icon = (nextCodePoint == 0x1f1fa) ? R.drawable.emoji_1f1f7_1f1fa : 0;\n                    break;\n                case 0x1f1e8:\n                    icon = (nextCodePoint == 0x1f1f3) ? R.drawable.emoji_1f1e8_1f1f3 : 0;\n                    break;\n                case 0x1f1f0:\n                    icon = (nextCodePoint == 0x1f1f7) ? R.drawable.emoji_1f1f0_1f1f7 : 0;\n                    break;\n                default:\n                    break;\n            }\n        }\n        return icon;\n    }\n\n    @Override\n    public int getQQfaceResource(CharSequence text) {\n        Integer integer = sQQFaceMap.get(text.toString());\n        if (integer == null) {\n            return 0;\n        }\n        return integer;\n    }\n\n    @Override\n    public Drawable queryForDrawable(CharSequence text) {\n        Integer integer = sQQFaceMap.get(text.toString());\n        if (integer == null) {\n            return null;\n        }\n        return ContextCompat.getDrawable(QDApplication.getContext(), integer);\n    }\n\n    @Override\n    public Drawable queryForDrawable(char c) {\n        int res = sSoftbanksMap.get(c);\n        if(res == 0){\n            return null;\n        }\n        return ContextCompat.getDrawable(QDApplication.getContext(), res);\n    }\n\n    @Override\n    public Drawable queryForDrawable(int codePoint) {\n        int res = sEmojisMap.get(codePoint);\n        if(res == 0){\n            return null;\n        }\n        return ContextCompat.getDrawable(QDApplication.getContext(), res);\n    }\n\n    @Override\n    public Drawable queryForDrawable(int firstCodePoint, int secondCodePint) {\n        int res = getDoubleUnicodeEmoji(firstCodePoint, secondCodePint);\n        if(res == 0){\n            return null;\n        }\n        return ContextCompat.getDrawable(QDApplication.getContext(), res);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/activity/ArchTestActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.activity;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.arch.annotation.ActivityScheme;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseActivity;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDArchTestFragment;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@ActivityScheme(name = \"arch\",\n        useRefreshIfCurrentMatched = true,\n        required = {\"aa\", \"bb=3\"},\n        keysWithBoolValue = {\"aa\"})\npublic class ArchTestActivity extends BaseActivity {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        View root = LayoutInflater.from(this).inflate(R.layout.activity_arch_test, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n        setContentView(root);\n    }\n\n    private void initTopBar() {\n        mTopBar.setBackgroundColor(ContextCompat.getColor(this, R.color.app_color_theme_4));\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                finish();\n                overridePendingTransition(R.anim.slide_still, R.anim.slide_out_right);\n            }\n        });\n        mTopBar.setTitle(\"Arch Test\");\n        QDArchTestFragment.injectEntrance(mTopBar);\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/activity/LauncherActivity.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.activity\n\nimport android.Manifest\nimport android.content.Intent\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.appcompat.app.AppCompatActivity\nimport com.qmuiteam.qmui.arch.QMUILatestVisit\nimport com.qmuiteam.qmui.arch.annotation.ActivityScheme\nimport com.qmuiteam.qmuidemo.QDMainActivity\n\n/**\n * @author cginechen\n * @date 2016-12-08\n */\n@ActivityScheme(name = \"launcher\")\nclass LauncherActivity : AppCompatActivity() {\n\n    private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {\n        if (it) {\n            var intent = QMUILatestVisit.intentOfLatestVisit(this)\n            if (intent == null) {\n                intent = Intent(this, QDMainActivity::class.java)\n            }\n            startActivity(intent)\n            finish()\n        } else {\n            Toast.makeText(this, \"Permissions not granted by the user.\", Toast.LENGTH_SHORT).show()\n            finish()\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n\n        super.onCreate(savedInstanceState)\n        if (intent.flags and Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT != 0) {\n            finish()\n            return\n        }\n        permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)\n    }\n\n    override fun finish() {\n        super.finish()\n        overridePendingTransition(0, 0)\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/activity/QDPhotoPickerActivity.kt",
    "content": "package com.qmuiteam.qmuidemo.activity\n\nimport com.qmuiteam.photo.activity.QMUIPhotoPickerActivity\n\nclass QDPhotoPickerActivity: QMUIPhotoPickerActivity() {\n\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/activity/TestArchInViewPagerActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.activity;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.QMUIFragmentPagerAdapter;\nimport com.qmuiteam.qmui.widget.QMUIViewPager;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseActivity;\nimport com.qmuiteam.qmuidemo.fragment.components.QDCollapsingTopBarLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDTabSegmentScrollableModeFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.viewpager.QDFitSystemWindowViewPagerFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.viewpager.QDViewPagerFragment;\n\nimport androidx.annotation.Nullable;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\npublic class TestArchInViewPagerActivity extends BaseActivity {\n\n    @BindView(R.id.pager) QMUIViewPager mViewPager;\n    @BindView(R.id.tabs) QMUITabSegment mTabSegment;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        View root = LayoutInflater.from(this).inflate(R.layout.fragment_fsw_viewpager, null);\n        ButterKnife.bind(this, root);\n        setContentView(root);\n        initPagers();\n    }\n\n    private void initPagers() {\n        QMUIFragmentPagerAdapter pagerAdapter = new QMUIFragmentPagerAdapter(getSupportFragmentManager()) {\n            @Override\n            public QMUIFragment createFragment(int position) {\n                switch (position) {\n                    case 0:\n                        return new QDTabSegmentScrollableModeFragment();\n                    case 1:\n                        return new QDCollapsingTopBarLayoutFragment();\n                    case 2:\n                        return new QDFitSystemWindowViewPagerFragment();\n                    case 3:\n                    default:\n                        return new QDViewPagerFragment();\n                }\n            }\n\n            @Override\n            public int getCount() {\n                return 4;\n            }\n\n            @Override\n            public CharSequence getPageTitle(int position) {\n                switch (position) {\n                    case 0:\n                        return \"TabSegment\";\n                    case 1:\n                        return \"CTopBar\";\n                    case 2:\n                        return \"IViewPager\";\n                    case 3:\n                    default:\n                        return \"ViewPager\";\n                }\n            }\n        };\n        mViewPager.setAdapter(pagerAdapter);\n        mTabSegment.setupWithViewPager(mViewPager);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/activity/TranslucentActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.activity;\n\nimport android.os.Bundle;\nimport androidx.core.content.ContextCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseActivity;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * 沉浸式状态栏的调用示例。\n * Created by Kayo on 2016/12/12.\n */\n\n@LatestVisitRecord\npublic class TranslucentActivity extends BaseActivity {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        View root = LayoutInflater.from(this).inflate(R.layout.activity_translucent, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n        setContentView(root);\n        if (getIntent().getBooleanExtra(\"test_activity\", false)) {\n            Toast.makeText(this, \"恢复到最近阅读(Boolean)\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                finish();\n                overridePendingTransition(R.anim.slide_still, R.anim.slide_out_right);\n            }\n        });\n\n        mTopBar.setTitle(\"沉浸式状态栏示例\");\n    }\n\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n        editor.putBoolean(\"test_activity\", true);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/adaptor/QDRecyclerViewAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.adaptor;\n\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmuidemo.R;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Demo 中通用的 RecyclerView Adapter。\n * Created by sm on 2015/5/3.\n */\npublic class QDRecyclerViewAdapter extends RecyclerView.Adapter<QDRecyclerViewAdapter.ViewHolder> {\n\n    private List<Data> mItems;\n    private AdapterView.OnItemClickListener mOnItemClickListener;\n\n    public QDRecyclerViewAdapter() {\n        mItems = new ArrayList<>();\n    }\n\n    public static List<Data> generateDatas(int count) {\n        ArrayList<Data> mDatas = new ArrayList<>();\n        for (int i = 0; i < count; i++) {\n            mDatas.add(new Data(String.valueOf(i)));\n        }\n        return mDatas;\n    }\n\n    public void addItem(int position) {\n        if (position > mItems.size()) return;\n\n        mItems.add(position, new Data(String.valueOf(position)));\n        notifyItemInserted(position);\n    }\n\n    public void removeItem(int position) {\n        if (position >= mItems.size()) return;\n\n        mItems.remove(position);\n        notifyItemRemoved(position);\n    }\n\n    @Override\n    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {\n        LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());\n        View root = inflater.inflate(R.layout.recycler_view_item, viewGroup, false);\n        return new ViewHolder(root, this);\n    }\n\n    @Override\n    public void onBindViewHolder(ViewHolder viewHolder, int i) {\n        Data data = mItems.get(i);\n        viewHolder.setText(data.text);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mItems.size();\n    }\n\n    public void setItemCount(int count) {\n        mItems.clear();\n        mItems.addAll(generateDatas(count));\n\n        notifyDataSetChanged();\n    }\n\n    public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {\n        mOnItemClickListener = onItemClickListener;\n    }\n\n    private void onItemHolderClick(RecyclerView.ViewHolder itemHolder) {\n        if (mOnItemClickListener != null) {\n            mOnItemClickListener.onItemClick(null, itemHolder.itemView,\n                    itemHolder.getAdapterPosition(), itemHolder.getItemId());\n        }\n    }\n\n    public static class Data {\n        public String text;\n\n        public Data(String text) {\n            this.text = text;\n        }\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {\n\n        private TextView mTextView;\n        private QDRecyclerViewAdapter mAdapter;\n\n        public ViewHolder(View itemView, QDRecyclerViewAdapter adapter) {\n            super(itemView);\n            itemView.setOnClickListener(this);\n\n            mAdapter = adapter;\n\n            mTextView = (TextView) itemView.findViewById(R.id.textView);\n        }\n\n        public void setText(String text) {\n            mTextView.setText(text);\n        }\n\n\n        @Override\n        public void onClick(View v) {\n            mAdapter.onItemHolderClick(this);\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/adaptor/QDSimpleAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.adaptor;\n\nimport android.content.Context;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.BaseAdapter;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmuidemo.R;\n\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2017-03-30\n */\n\npublic class QDSimpleAdapter extends BaseAdapter {\n    private Context mContext;\n    private List<String> mData;\n\n    public QDSimpleAdapter(Context context, List<String> data) {\n        mContext = context;\n        mData = data;\n    }\n\n    public void setData(List<String> data) {\n        mData = data;\n    }\n\n    @Override\n    public int getCount() {\n        return mData.size();\n    }\n\n    @Override\n    public String getItem(int position) {\n        return mData.get(position);\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return position;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        ItemView itemView;\n        if (convertView == null) {\n            itemView = new ItemView(mContext);\n            itemView.setLayoutParams(new AbsListView.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n\n        } else {\n            itemView = (ItemView) convertView;\n        }\n        itemView.setText(getItem(position));\n        return itemView;\n    }\n\n    public void remove(int position) {\n        mData.remove(position);\n        notifyDataSetChanged();\n    }\n\n    static class ItemView extends FrameLayout {\n        private TextView textView;\n\n        public ItemView(Context context) {\n            super(context);\n            textView = new TextView(context);\n            int paddingHor = QMUIDisplayHelper.dp2px(context, 12);\n            int paddingVer = QMUIDisplayHelper.dp2px(context, 6);\n            setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n            addView(textView, new FrameLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, QMUIDisplayHelper.dp2px(context, 64)));\n            int paddingTvHor = QMUIDisplayHelper.dp2px(context, 16);\n            QMUIViewHelper.setBackgroundKeepingPadding(textView, QMUIResHelper.getAttrDrawable(context, R.attr.qmui_skin_support_s_list_item_bg_1));\n            textView.setPadding(paddingTvHor, 0, paddingTvHor, 0);\n            textView.setGravity(Gravity.CENTER_VERTICAL);\n        }\n\n        public void setText(String text) {\n            textView.setText(text);\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/BaseActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.base;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\n\nimport com.qmuiteam.qmui.arch.QMUIActivity;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.manager.QDUpgradeManager;\n\nimport static com.qmuiteam.qmuidemo.QDApplication.getContext;\n\n@SuppressLint(\"Registered\")\npublic class BaseActivity extends QMUIActivity {\n\n    @Override\n    protected int backViewInitOffset() {\n        return QMUIDisplayHelper.dp2px(getContext(), 100);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        QDUpgradeManager.getInstance(getContext()).runUpgradeTipTaskIfExist(this);\n    }\n\n    @Override\n    public Intent onLastActivityFinish() {\n        return new Intent(this, QDMainActivity.class);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/BaseFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.base;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.util.Log;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.SwipeBackLayout;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.fragment.home.HomeFragment;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.manager.QDUpgradeManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\n/**\n * Created by cgspine on 2018/1/7.\n */\n\npublic abstract class BaseFragment extends QMUIFragment {\n\n    private static final String TAG = \"BaseFragment\";\n\n\n    public BaseFragment() {\n    }\n\n    @Override\n    protected int backViewInitOffset(Context context, int dragDirection, int moveEdge) {\n        if (moveEdge == SwipeBackLayout.EDGE_TOP || moveEdge == SwipeBackLayout.EDGE_BOTTOM) {\n            return 0;\n        }\n        return QMUIDisplayHelper.dp2px(context, 100);\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        QDUpgradeManager.getInstance(getContext()).runUpgradeTipTaskIfExist(getActivity());\n        Log.i(TAG, getClass().getSimpleName() + \" onResume\");\n\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        Log.i(TAG, getClass().getSimpleName() + \" onStart\");\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        Log.i(TAG, getClass().getSimpleName() + \" onPause\");\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        Log.i(TAG, getClass().getSimpleName() + \" onStop\");\n    }\n\n    @Override\n    public Object onLastFragmentFinish() {\n        return new HomeFragment();\n    }\n\n    protected void goToWebExplorer(@NonNull String url, @Nullable String title) {\n        Intent intent = QDMainActivity.createWebExplorerIntent(getContext(), url, title);\n        startActivity(intent);\n    }\n\n    protected void injectDocToTopBar(QMUITopBar topBar) {\n        final QDItemDescription description = QDDataManager.getInstance().getDescription(this.getClass());\n        if (description != null) {\n            topBar.addRightTextButton(\"DOC\", QMUIViewHelper.generateViewId())\n                    .setOnClickListener(new View.OnClickListener() {\n                        @Override\n                        public void onClick(View v) {\n                            goToWebExplorer(description.getDocUrl(), description.getName());\n                        }\n                    });\n        }\n    }\n\n    protected void injectDocToTopBar(QMUITopBarLayout topBar) {\n        final QDItemDescription description = QDDataManager.getInstance().getDescription(this.getClass());\n        if (description != null) {\n            topBar.addRightTextButton(\"DOC\", QMUIViewHelper.generateViewId())\n                    .setOnClickListener(new View.OnClickListener() {\n                        @Override\n                        public void onClick(View v) {\n                            goToWebExplorer(description.getDocUrl(), description.getName());\n                        }\n                    });\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/BaseFragmentActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.base;\n\nimport com.qmuiteam.qmui.arch.QMUIFragmentActivity;\n\n/**\n * Created by cgspine on 2018/1/7.\n */\n\npublic abstract class BaseFragmentActivity extends QMUIFragmentActivity {\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/BaseRecyclerAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.base;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2016-10-19\n */\n\npublic abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {\n    private List<T> mData = new ArrayList<>();\n    private final Context mContext;\n    private LayoutInflater mInflater;\n    private OnItemClickListener mClickListener;\n    private OnItemLongClickListener mLongClickListener;\n\n    public BaseRecyclerAdapter(Context ctx, @Nullable List<T> list) {\n        if(list != null){\n            mData.addAll(list);\n        }\n        mContext = ctx;\n        mInflater = LayoutInflater.from(ctx);\n    }\n\n    public void setData(@Nullable List<T> list) {\n        mData.clear();\n        if(list != null){\n            mData.addAll(list);\n        }\n        notifyDataSetChanged();\n    }\n\n    public void remove(int pos){\n        mData.remove(pos);\n        notifyItemRemoved(pos);\n    }\n\n    @Override\n    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        final RecyclerViewHolder holder = new RecyclerViewHolder(mContext,\n                mInflater.inflate(getItemLayoutId(viewType), parent, false));\n        if (mClickListener != null) {\n            holder.itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    mClickListener.onItemClick(holder.itemView, holder.getLayoutPosition());\n                }\n            });\n        }\n        if (mLongClickListener != null) {\n            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {\n                @Override\n                public boolean onLongClick(View v) {\n                    mLongClickListener.onItemLongClick(holder.itemView, holder.getLayoutPosition());\n                    return true;\n                }\n            });\n        }\n        return holder;\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {\n        bindData(holder, position, mData.get(position));\n    }\n\n    public T getItem(int pos){\n        return mData.get(pos);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mData.size();\n    }\n\n    public void add(int pos, T item) {\n        mData.add(pos, item);\n        notifyItemInserted(pos);\n    }\n\n    public void prepend(@NonNull List<T> items){\n        mData.addAll(0, items);\n        notifyDataSetChanged();\n    }\n\n    public void append(@NonNull List<T> items){\n        mData.addAll(items);\n        notifyDataSetChanged();\n    }\n\n    public void delete(int pos) {\n        mData.remove(pos);\n        notifyItemRemoved(pos);\n    }\n\n    public void setOnItemClickListener(OnItemClickListener listener) {\n        mClickListener = listener;\n    }\n\n    public void setOnItemLongClickListener(OnItemLongClickListener listener) {\n        mLongClickListener = listener;\n    }\n\n    @SuppressWarnings(\"SameReturnValue\")\n    abstract public int getItemLayoutId(int viewType);\n\n    abstract public void bindData(@NonNull RecyclerViewHolder holder, int position, @NonNull T item);\n\n    public interface OnItemClickListener {\n        void onItemClick(View itemView, int pos);\n    }\n\n    public interface OnItemLongClickListener {\n        void onItemLongClick(View itemView, int pos);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/ComposeBaseFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.base\n\nimport android.view.View\nimport android.widget.FrameLayout\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.platform.ComposeView\nimport androidx.lifecycle.ViewTreeLifecycleOwner\nimport androidx.lifecycle.ViewTreeViewModelStoreOwner\nimport androidx.savedstate.setViewTreeSavedStateRegistryOwner\nimport com.qmuiteam.compose.core.provider.QMUIWindowInsetsProvider\nimport com.qmuiteam.qmui.kotlin.matchParent\n\nabstract class ComposeBaseFragment(): BaseFragment() {\n    override fun onCreateView(): View {\n        return object: FrameLayout(requireContext()){\n\n            private val composeView = ComposeView(requireContext()).apply {\n                setContent {\n                    QMUIWindowInsetsProvider {\n                        PageContent()\n                    }\n                }\n            }.apply {\n                ViewTreeLifecycleOwner.set(this, this@ComposeBaseFragment)\n                ViewTreeViewModelStoreOwner.set(this, this@ComposeBaseFragment)\n                setViewTreeSavedStateRegistryOwner(this@ComposeBaseFragment)\n            }\n\n            init {\n                addView(composeView, LayoutParams(matchParent, matchParent))\n            }\n\n            override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n                val wm = MeasureSpec.getMode(widthMeasureSpec)\n                val ws = MeasureSpec.getSize(widthMeasureSpec).coerceAtMost(0x2FFFF)\n                val hm = MeasureSpec.getMode(heightMeasureSpec)\n                val hs = MeasureSpec.getSize(heightMeasureSpec).coerceAtMost(0x2FFFF)\n                super.onMeasure(\n                    MeasureSpec.makeMeasureSpec(ws, wm),\n                    MeasureSpec.makeMeasureSpec(hs, hm)\n                )\n            }\n        }\n    }\n\n    @Composable\n    protected abstract fun PageContent()\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/base/RecyclerViewHolder.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.base;\n\nimport android.content.Context;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.util.SparseArray;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ImageButton;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\n/**\n * @author cginechen\n * @date 2016-10-19\n */\n\npublic class RecyclerViewHolder extends RecyclerView.ViewHolder {\n    private SparseArray<View> mViews;\n\n    public RecyclerViewHolder(Context ctx, View itemView) {\n        super(itemView);\n        mViews = new SparseArray<>();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T extends View> T findViewById(int viewId) {\n        View view = mViews.get(viewId);\n        if (view == null) {\n            view = itemView.findViewById(viewId);\n            mViews.put(viewId, view);\n        }\n        return (T) view;\n    }\n\n    public View getView(int viewId) {\n        return findViewById(viewId);\n    }\n\n    public TextView getTextView(int viewId) {\n        return (TextView) getView(viewId);\n    }\n\n    public Button getButton(int viewId) {\n        return (Button) getView(viewId);\n    }\n\n    public ImageView getImageView(int viewId) {\n        return (ImageView) getView(viewId);\n    }\n\n    public ImageButton getImageButton(int viewId) {\n        return (ImageButton) getView(viewId);\n    }\n\n    public EditText getEditText(int viewId) {\n        return (EditText) getView(viewId);\n    }\n\n    public RecyclerViewHolder setText(int viewId, String value) {\n        TextView view = findViewById(viewId);\n        view.setText(value);\n        return this;\n    }\n\n    public RecyclerViewHolder setBackground(int viewId, int resId) {\n        View view = findViewById(viewId);\n        view.setBackgroundResource(resId);\n        return this;\n    }\n\n    public RecyclerViewHolder setClickListener(int viewId, View.OnClickListener listener) {\n        View view = findViewById(viewId);\n        view.setOnClickListener(listener);\n        return this;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/decorator/DividerItemDecoration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.decorator;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.View;\n\n/**\n * @author cginechen\n * @date 2016-10-21\n */\n\npublic class DividerItemDecoration extends RecyclerView.ItemDecoration {\n    private static final int[] ATTRS = new int[]{\n            android.R.attr.listDivider\n    };\n    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;\n    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;\n    private Drawable mDivider;\n    private int mOrientation;\n    public DividerItemDecoration(Context context, int orientation) {\n        final TypedArray a = context.obtainStyledAttributes(ATTRS);\n        mDivider = a.getDrawable(0);\n        a.recycle();\n        setOrientation(orientation);\n    }\n    public void setOrientation(int orientation) {\n        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {\n            throw new IllegalArgumentException(\"invalid orientation\");\n        }\n        mOrientation = orientation;\n    }\n\n    @Override\n    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL_LIST) {\n            drawVertical(c, parent);\n        } else {\n            drawHorizontal(c, parent);\n        }\n    }\n\n    public void drawVertical(Canvas c, RecyclerView parent) {\n        final int left = parent.getPaddingLeft();\n        final int right = parent.getWidth() - parent.getPaddingRight();\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int top = child.getBottom() + params.bottomMargin +\n                    Math.round(ViewCompat.getTranslationY(child));\n            final int bottom = top + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n    public void drawHorizontal(Canvas c, RecyclerView parent) {\n        final int top = parent.getPaddingTop();\n        final int bottom = parent.getHeight() - parent.getPaddingBottom();\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int left = child.getRight() + params.rightMargin +\n                    Math.round(ViewCompat.getTranslationX(child));\n            final int right = left + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL_LIST) {\n            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());\n        } else {\n            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/decorator/GridDividerItemDecoration.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.decorator;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.IQMUISkinHandlerDecoration;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmuidemo.R;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * @author cginechen\n * @date 2016-10-21\n */\n\npublic class GridDividerItemDecoration extends RecyclerView.ItemDecoration implements IQMUISkinHandlerDecoration {\n\n    private Paint mDividerPaint = new Paint();\n    private int mSpanCount;\n    private final int mDividerAttr;\n\n    public GridDividerItemDecoration(Context context, int spanCount) {\n        this(context, spanCount, R.attr.qmui_skin_support_color_separator, 1f);\n    }\n\n    public GridDividerItemDecoration(Context context, int spanCount, int dividerColorAttr, float dividerWidth) {\n        mSpanCount = spanCount;\n        mDividerAttr = dividerColorAttr;\n        mDividerPaint.setStrokeWidth(dividerWidth);\n        mDividerPaint.setStyle(Paint.Style.STROKE);\n        mDividerPaint.setColor(QMUIResHelper.getAttrColor(context, dividerColorAttr));\n    }\n\n    @Override\n    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        super.onDrawOver(c, parent, state);\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            int position = parent.getChildLayoutPosition(child);\n            int column = (position + 1) % mSpanCount;\n\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int childBottom = child.getBottom() + params.bottomMargin +\n                    Math.round(ViewCompat.getTranslationY(child));\n            final int childRight = child.getRight() + params.rightMargin +\n                    Math.round(ViewCompat.getTranslationX(child));\n\n            if (childBottom < parent.getHeight()) {\n                c.drawLine(child.getLeft(), childBottom, childRight, childBottom, mDividerPaint);\n            }\n\n            if (column < mSpanCount) {\n                c.drawLine(childRight, child.getTop(), childRight, childBottom, mDividerPaint);\n            }\n\n        }\n    }\n\n    @Override\n    public void handle(@NotNull RecyclerView recyclerView, @NotNull QMUISkinManager manager, int skinIndex, @NotNull Resources.Theme theme) {\n        mDividerPaint.setColor(QMUIResHelper.getAttrColor(theme, mDividerAttr));\n        recyclerView.invalidate();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/QDAboutFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment\n\nimport android.os.Bundle\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport butterknife.BindView\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.TextView\nimport butterknife.ButterKnife\nimport com.qmuiteam.qmui.util.QMUIPackageHelper\nimport com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment\nimport com.qmuiteam.qmui.arch.QMUIFragment\nimport com.qmuiteam.qmui.arch.QMUIFragment.TransitionConfig\nimport com.qmuiteam.qmui.arch.SwipeBackLayout.ViewMoveAction\nimport com.qmuiteam.qmui.arch.SwipeBackLayout\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.manager.QDSchemeManager\nimport java.text.SimpleDateFormat\nimport java.util.*\n\n/**\n * 关于界面\n *\n *\n * Created by Kayo on 2016/11/18.\n */\nclass QDAboutFragment : BaseFragment() {\n    @JvmField\n    @BindView(R.id.topbar)\n    var mTopBar: QMUITopBarLayout? = null\n\n    @JvmField\n    @BindView(R.id.version)\n    var mVersionTextView: TextView? = null\n\n    @JvmField\n    @BindView(R.id.about_list)\n    var mAboutGroupListView: QMUIGroupListView? = null\n\n    @JvmField\n    @BindView(R.id.copyright)\n    var mCopyrightTextView: TextView? = null\n    override fun onCreateView(): View {\n        val root = LayoutInflater.from(activity).inflate(R.layout.fragment_about, null)\n        ButterKnife.bind(this, root)\n        initTopBar()\n        mVersionTextView!!.text = QMUIPackageHelper.getAppVersion(context)\n        QMUIGroupListView.newSection(context)\n            .addItemView(mAboutGroupListView!!.createItemView(resources.getString(R.string.about_item_homepage))) {\n                val url = \"https://qmuiteam.com/android\"\n                val bundle = Bundle()\n                bundle.putString(QDWebExplorerFragment.EXTRA_URL, url)\n                bundle.putString(QDWebExplorerFragment.EXTRA_TITLE, resources.getString(R.string.about_item_homepage))\n                val fragment: QMUIFragment = QDWebExplorerFragment()\n                fragment.arguments = bundle\n                startFragment(fragment)\n            }\n            .addItemView(mAboutGroupListView!!.createItemView(resources.getString(R.string.about_item_github))) {\n                val url = \"https://github.com/Tencent/QMUI_Android\"\n                val bundle = Bundle()\n                bundle.putString(QDWebExplorerFragment.EXTRA_URL, url)\n                bundle.putString(QDWebExplorerFragment.EXTRA_TITLE, resources.getString(R.string.about_item_github))\n                val fragment: QMUIFragment = QDWebExplorerFragment()\n                fragment.arguments = bundle\n                startFragment(fragment)\n            }\n            .addTo(mAboutGroupListView)\n        val dateFormat = SimpleDateFormat(\"yyyy\", Locale.CHINA)\n        val currentYear = dateFormat.format(Date())\n        mCopyrightTextView!!.text = String.format(resources.getString(R.string.about_copyright), currentYear)\n        return root\n    }\n\n    private fun initTopBar() {\n        mTopBar!!.addLeftBackImageButton().setOnClickListener { popBackStack() }\n        mTopBar!!.setTitle(resources.getString(R.string.about_title))\n    }\n\n    override fun onFetchTransitionConfig(): TransitionConfig {\n        return SCALE_TRANSITION_CONFIG\n    }\n\n    override fun dragViewMoveAction(): ViewMoveAction {\n        return SwipeBackLayout.MOVE_VIEW_TOP_TO_BOTTOM\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/QDDialogFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment\n\nimport android.widget.Toast\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.LocalView\nimport androidx.compose.ui.unit.dp\nimport com.qmuiteam.compose.core.ex.drawBottomSeparator\nimport com.qmuiteam.compose.modal.*\nimport com.qmuiteam.compose.core.ui.*\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.ComposeBaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\n\n\n@Widget(widgetClass = QMUIDialog::class, iconRes = R.mipmap.icon_grid_dialog)\n@LatestVisitRecord\nclass QDDialogFragment() : ComposeBaseFragment() {\n\n    @Composable\n    override fun PageContent() {\n        Column(modifier = Modifier.fillMaxSize()) {\n            val scrollState = rememberLazyListState()\n            QMUITopBarWithLazyScrollState(\n                scrollState = scrollState,\n                title = \"QMUIDialog\",\n                leftItems = arrayListOf(\n                    QMUITopBarBackIconItem {\n                        popBackStack()\n                    }\n                ),\n                rightItems = arrayListOf(\n                    QMUITopBarTextItem(\"Test\") {\n                        startFragment(QDAboutFragment())\n                    }\n                )\n            )\n            val view = LocalView.current\n            LazyColumn(\n                state = scrollState,\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .weight(1f)\n                    .background(Color.White)\n            ) {\n                item {\n                    QMUIItem(\n                        title = \"消息类型对话框\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiDialog { modal ->\n                            QMUIDialogMsg(modal,\n                                \"这是标题\",\n                                \"这是一丢丢有趣但是没啥用的内容\",\n                                listOf(\n                                    QMUIModalAction(\"取 消\") {\n                                        it.dismiss()\n                                    },\n                                    QMUIModalAction(\"确 定\") {\n                                        Toast\n                                            .makeText(view.context, \"确定啦!!!\", Toast.LENGTH_SHORT)\n                                            .show()\n                                        it.dismiss()\n                                    }\n                                )\n                            )\n                        }.show()\n                    }\n                }\n\n                item {\n                    QMUIItem(\n                        title = \"列表类型对话框\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiDialog { modal ->\n                            QMUIDialogList(modal, maxHeight = 500.dp) {\n                                items(200){ index ->\n                                    QMUIItem(title = \"第${index + 1}项\") {\n                                        Toast.makeText(view.context, \"你点了第${index + 1}项\", Toast.LENGTH_SHORT).show()\n                                    }\n                                }\n                            }\n                        }.show()\n                    }\n                }\n\n                item {\n                    QMUIItem(\n                        title = \"单选类型浮层\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiDialog { modal ->\n                            val list = remember {\n                                val items = arrayListOf<String>()\n                                for(i in 0 until 500){\n                                    items.add(\"Item $i\")\n                                }\n                                items\n                            }\n                            val markIndex by remember {\n                                mutableStateOf(20)\n                            }\n                            QMUIDialogMarkList(\n                                modal,\n                                maxHeight = 500.dp,\n                                list = list,\n                                markIndex = markIndex\n                            ) { _, index ->\n                                Toast.makeText(view.context, \"你点了第${index + 1}项\", Toast.LENGTH_SHORT).show()\n//                                modal.dismiss()\n                            }\n                        }.show()\n                    }\n                }\n\n                item {\n                    QMUIItem(\n                        title = \"多选类型浮层\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiDialog { modal ->\n                            val list = remember {\n                                val items = arrayListOf<String>()\n                                for(i in 0 until 500){\n                                    items.add(\"Item $i\")\n                                }\n                                items\n                            }\n                            val checked = remember {\n                                mutableStateListOf(0, 5, 10, 20)\n                            }\n                            val disable = remember {\n                                mutableStateListOf(5, 10)\n                            }\n                            Column() {\n                                QMUIDialogMutiCheckList(\n                                    modal,\n                                    maxHeight = 500.dp,\n                                    list = list,\n                                    checked = checked.toSet(),\n                                    disabled = disable.toSet()\n                                ) { _, index ->\n                                    if(checked.contains(index)){\n                                        checked.remove(index)\n                                    }else{\n                                        checked.add(index)\n                                    }\n                                }\n                                QMUIDialogActions(modal = modal, actions = listOf(\n                                    QMUIModalAction(\"取 消\") {\n                                        it.dismiss()\n                                    },\n                                    QMUIModalAction(\"确 定\") {\n                                        Toast\n                                            .makeText(view.context, \"你选择了: ${checked.joinToString(\",\")}\", Toast.LENGTH_SHORT)\n                                            .show()\n                                        it.dismiss()\n                                    }\n                                ))\n                            }\n                        }.show()\n                    }\n                }\n\n                item {\n                    QMUIItem(\n                        title = \"Toast\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiToast(\"这只是个 Toast!\")\n                    }\n                }\n\n                item {\n                    QMUIItem(\n                        title = \"BottomSheet(list)\",\n                        drawBehind = {\n                            drawBottomSeparator(insetStart = qmuiCommonHorSpace, insetEnd = qmuiCommonHorSpace)\n                        }\n                    ) {\n                        view.qmuiBottomSheet {\n                            QMUIBottomSheetList(it) {\n                                items(200){ index ->\n                                    QMUIItem(title = \"第${index + 1}项\") {\n                                        Toast.makeText(view.context, \"你点了第${index + 1}项\", Toast.LENGTH_SHORT).show()\n                                    }\n                                }\n                            }\n                        }.show()\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/QDWebExplorerFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ObjectAnimator;\nimport android.graphics.Bitmap;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.webkit.DownloadListener;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\nimport android.widget.ProgressBar;\nimport android.widget.ZoomButtonsController;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewClient;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewContainer;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.view.QDWebView;\n\nimport java.io.UnsupportedEncodingException;\nimport java.lang.reflect.Field;\nimport java.net.URLDecoder;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * Created by cgspine on 2017/12/4.\n */\n\npublic class QDWebExplorerFragment extends BaseFragment {\n    public static final String EXTRA_URL = \"EXTRA_URL\";\n    public static final String EXTRA_TITLE = \"EXTRA_TITLE\";\n    public static final String EXTRA_NEED_DECODE = \"EXTRA_NEED_DECODE\";\n\n    private final static int PROGRESS_PROCESS = 0;\n    private final static int PROGRESS_GONE = 1;\n\n\n    @BindView(R.id.topbar) protected QMUITopBarLayout mTopBarLayout;\n    @BindView(R.id.webview_container) QMUIWebViewContainer mWebViewContainer;\n    @BindView(R.id.progress_bar) ProgressBar mProgressBar;\n    protected QDWebView mWebView;\n\n\n    private String mUrl;\n    private String mTitle;\n    private ProgressHandler mProgressHandler;\n    private boolean mIsPageFinished = false;\n    private boolean mNeedDecodeUrl = false;\n\n    @Override\n    protected View onCreateView() {\n        Bundle bundle = getArguments();\n        if (bundle != null) {\n            String url = bundle.getString(EXTRA_URL);\n            mTitle = bundle.getString(EXTRA_TITLE);\n            mNeedDecodeUrl = bundle.getBoolean(EXTRA_NEED_DECODE, false);\n            if (url != null && url.length() > 0) {\n                handleUrl(url);\n            }\n        }\n\n        mProgressHandler = new ProgressHandler();\n\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_webview_explorer, null);\n        ButterKnife.bind(this, view);\n        initTopbar();\n        initWebView();\n        return view;\n    }\n\n    protected void initTopbar() {\n        mTopBarLayout.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n        updateTitle(mTitle);\n    }\n\n    private void updateTitle(String title) {\n        if (title != null && !title.equals(\"\")) {\n            mTitle = title;\n            mTopBarLayout.setTitle(mTitle);\n        }\n    }\n\n    protected boolean needDispatchSafeAreaInset() {\n        return false;\n    }\n\n    protected void initWebView() {\n        mWebView = new QDWebView(getContext());\n        boolean needDispatchSafeAreaInset = needDispatchSafeAreaInset();\n        mWebViewContainer.addWebView(mWebView, needDispatchSafeAreaInset);\n        mWebViewContainer.setCustomOnScrollChangeListener(new QMUIWebView.OnScrollChangeListener() {\n            @Override\n            public void onScrollChange(WebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n                onScrollWebContent(scrollX, scrollY, oldScrollX, oldScrollY);\n            }\n        });\n        FrameLayout.LayoutParams containerLp = (FrameLayout.LayoutParams) mWebViewContainer.getLayoutParams();\n        mWebViewContainer.setFitsSystemWindows(!needDispatchSafeAreaInset);\n        containerLp.topMargin = needDispatchSafeAreaInset ? 0 : QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_topbar_height);\n        mWebViewContainer.setLayoutParams(containerLp);\n\n        mWebView.setDownloadListener(new DownloadListener() {\n            @Override\n            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {\n                boolean needConfirm = !url.startsWith(\"http://qmuiteam.com\") && !url.startsWith(\"https://qmuiteam.com\");\n                if (needConfirm) {\n                    final String finalURL = url;\n                    new QMUIDialog.MessageDialogBuilder(getContext())\n                            .setMessage(\"确认下载此文件？\")\n                            .addAction(R.string.cancel, new QMUIDialogAction.ActionListener() {\n                                @Override\n                                public void onClick(QMUIDialog dialog, int index) {\n                                    dialog.dismiss();\n                                    popBackStack();\n                                }\n                            })\n                            .addAction(R.string.ok, new QMUIDialogAction.ActionListener() {\n                                @Override\n                                public void onClick(QMUIDialog dialog, int index) {\n                                    dialog.dismiss();\n                                    doDownload(finalURL);\n                                    popBackStack();\n                                }\n                            })\n                            .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                            .show();\n                } else {\n                    doDownload(url);\n                }\n            }\n\n            private void doDownload(String url) {\n\n            }\n        });\n\n        mWebView.setWebChromeClient(getWebViewChromeClient());\n        mWebView.setWebViewClient(getWebViewClient());\n        mWebView.requestFocus(View.FOCUS_DOWN);\n        setZoomControlGone(mWebView);\n        configWebView(mWebViewContainer, mWebView);\n        mWebView.loadUrl(mUrl);\n    }\n\n    protected void configWebView(QMUIWebViewContainer webViewContainer, QMUIWebView webView) {\n\n    }\n\n    protected void onScrollWebContent(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n\n    }\n\n    private void handleUrl(String url) {\n        if (mNeedDecodeUrl) {\n            String decodeURL;\n            try {\n                decodeURL = URLDecoder.decode(url, \"utf-8\");\n            } catch (UnsupportedEncodingException ignored) {\n                decodeURL = url;\n            }\n            mUrl = decodeURL;\n        } else {\n            mUrl = url;\n        }\n    }\n\n    protected WebChromeClient getWebViewChromeClient() {\n        return new ExplorerWebViewChromeClient(this);\n    }\n\n    protected QMUIWebViewClient getWebViewClient() {\n        return new ExplorerWebViewClient(needDispatchSafeAreaInset());\n    }\n\n    private void sendProgressMessage(int progressType, int newProgress, int duration) {\n        Message msg = new Message();\n        msg.what = progressType;\n        msg.arg1 = newProgress;\n        msg.arg2 = duration;\n        mProgressHandler.sendMessage(msg);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        mWebViewContainer.destroy();\n        mWebView = null;\n    }\n\n    public static void setZoomControlGone(WebView webView) {\n        webView.getSettings().setDisplayZoomControls(false);\n        @SuppressWarnings(\"rawtypes\")\n        Class classType;\n        Field field;\n        try {\n            classType = WebView.class;\n            field = classType.getDeclaredField(\"mZoomButtonsController\");\n            field.setAccessible(true);\n            ZoomButtonsController zoomButtonsController = new ZoomButtonsController(\n                    webView);\n            zoomButtonsController.getZoomControls().setVisibility(View.GONE);\n            try {\n                field.set(webView, zoomButtonsController);\n            } catch (IllegalArgumentException | IllegalAccessException e) {\n                e.printStackTrace();\n            }\n        } catch (SecurityException | NoSuchFieldException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static class ExplorerWebViewChromeClient extends WebChromeClient {\n        private QDWebExplorerFragment mFragment;\n\n        public ExplorerWebViewChromeClient(QDWebExplorerFragment fragment) {\n            mFragment = fragment;\n        }\n\n        @Override\n        public void onProgressChanged(WebView view, int newProgress) {\n            super.onProgressChanged(view, newProgress);\n            // 修改进度条\n            if (newProgress > mFragment.mProgressHandler.mDstProgressIndex) {\n                mFragment.sendProgressMessage(PROGRESS_PROCESS, newProgress, 100);\n            }\n        }\n\n        @Override\n        public void onReceivedTitle(WebView view, String title) {\n            super.onReceivedTitle(view, title);\n            mFragment.updateTitle(view.getTitle());\n        }\n\n        @Override\n        public void onShowCustomView(View view, CustomViewCallback callback) {\n            callback.onCustomViewHidden();\n        }\n\n        @Override\n        public void onHideCustomView() {\n\n        }\n    }\n\n    protected class ExplorerWebViewClient extends QMUIWebViewClient {\n\n        public ExplorerWebViewClient(boolean needDispatchSafeAreaInset) {\n            super(needDispatchSafeAreaInset, true);\n        }\n\n        @Override\n        public void onPageStarted(WebView view, String url, Bitmap favicon) {\n            super.onPageStarted(view, url, favicon);\n            if (QMUILangHelper.isNullOrEmpty(mTitle)) {\n                updateTitle(view.getTitle());\n            }\n            if (mProgressHandler.mDstProgressIndex == 0) {\n                sendProgressMessage(PROGRESS_PROCESS, 30, 500);\n            }\n        }\n\n        @Override\n        public void onPageFinished(WebView view, String url) {\n            super.onPageFinished(view, url);\n            sendProgressMessage(PROGRESS_GONE, 100, 0);\n            if (QMUILangHelper.isNullOrEmpty(mTitle)) {\n                updateTitle(view.getTitle());\n            }\n        }\n    }\n\n    private class ProgressHandler extends Handler {\n\n        private int mDstProgressIndex;\n        private int mDuration;\n        private ObjectAnimator mAnimator;\n\n\n        @Override\n        public void handleMessage(Message msg) {\n            switch (msg.what) {\n                case PROGRESS_PROCESS:\n                    mIsPageFinished = false;\n                    mDstProgressIndex = msg.arg1;\n                    mDuration = msg.arg2;\n                    mProgressBar.setVisibility(View.VISIBLE);\n                    if (mAnimator != null && mAnimator.isRunning()) {\n                        mAnimator.cancel();\n                    }\n                    mAnimator = ObjectAnimator.ofInt(mProgressBar, \"progress\", mDstProgressIndex);\n                    mAnimator.setDuration(mDuration);\n                    mAnimator.addListener(new AnimatorListenerAdapter() {\n                        @Override\n                        public void onAnimationEnd(Animator animation) {\n                            if (mProgressBar.getProgress() == 100) {\n                                sendEmptyMessageDelayed(PROGRESS_GONE, 500);\n                            }\n                        }\n                    });\n                    mAnimator.start();\n                    break;\n                case PROGRESS_GONE:\n                    mDstProgressIndex = 0;\n                    mDuration = 0;\n                    mProgressBar.setProgress(0);\n                    mProgressBar.setVisibility(View.GONE);\n                    if (mAnimator != null && mAnimator.isRunning()) {\n                        mAnimator.cancel();\n                    }\n                    mAnimator = ObjectAnimator.ofInt(mProgressBar, \"progress\", 0);\n                    mAnimator.setDuration(0);\n                    mAnimator.removeAllListeners();\n                    mIsPageFinished = true;\n                    break;\n                default:\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDBottomSheetFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIBottomSheet} 的使用示例。\n * Created by cgspine on 15/9/15.\n */\n\n@Widget(widgetClass = QMUIBottomSheet.class, iconRes = R.mipmap.icon_grid_bottom_sheet)\npublic class QDBottomSheetFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.listview)\n    ListView mListView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_listview, null);\n        ButterKnife.bind(this, view);\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initListView();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n\n    private void initListView() {\n        String[] listItems = new String[]{\n                \"BottomSheet List:Simple\",\n                \"BottomSheet List:With Icon\",\n                \"BottomSheet List:GravityCenter\",\n                \"BottomSheet List:With Title\",\n                \"BottomSheet List:With Cancel Btn\",\n                \"BottomSheet List:Drag Dismiss\",\n                \"BottomSheet List:Many Items\",\n                \"BottomSheet List:With Mark\",\n                \"BottomSheet Grid\"\n        };\n        List<String> data = new ArrayList<>();\n\n        Collections.addAll(data, listItems);\n\n        mListView.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.simple_list_item, data));\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                switch (position) {\n                    case 0:\n                        showSimpleBottomSheetList(\n                                false, false, false, null,\n                                3, false, false);\n                        break;\n                    case 1:\n                        showSimpleBottomSheetList(\n                                false, false, true, null,\n                                3, false, false);\n                        break;\n                    case 2:\n                        showSimpleBottomSheetList(\n                                true, false, false, null,\n                                3, false, false);\n                        break;\n                    case 3:\n                        showSimpleBottomSheetList(\n                                true, false, false, \"This is Title!!!\",\n                                3, false, false);\n                        break;\n                    case 4:\n                        showSimpleBottomSheetList(\n                                true, true, false, \"This is Title!!!\",\n                                3, false, false);\n                        break;\n                    case 5:\n                        showSimpleBottomSheetList(\n                                true, true, false, \"This is Title!!!\",\n                                3, true, false);\n                        break;\n                    case 6:\n                        showSimpleBottomSheetList(\n                                true, true, false, \"This is Title!!!\",\n                                100, true, false);\n                        break;\n                    case 7:\n                        showSimpleBottomSheetList(\n                                false, true, false, \"This is Title!!!\",\n                                100, true, true);\n                        break;\n                    case 8:\n                        showSimpleBottomSheetGrid();\n                        break;\n                }\n            }\n        });\n    }\n\n    // ================================ 生成不同类型的BottomSheet\n    private void showSimpleBottomSheetList(boolean gravityCenter,\n                                           boolean addCancelBtn,\n                                           boolean withIcon,\n                                           CharSequence title,\n                                           int itemCount,\n                                           boolean allowDragDismiss,\n                                           boolean withMark) {\n        QMUIBottomSheet.BottomListSheetBuilder builder = new QMUIBottomSheet.BottomListSheetBuilder(getActivity());\n        builder.setGravityCenter(gravityCenter)\n                .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                .setTitle(title)\n                .setAddCancelBtn(addCancelBtn)\n                .setAllowDrag(allowDragDismiss)\n                .setNeedRightMark(withMark)\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        Toast.makeText(getActivity(), \"Item \" + (position + 1), Toast.LENGTH_SHORT).show();\n                    }\n                });\n        if(withMark){\n            builder.setCheckedIndex(40);\n        }\n        for (int i = 1; i <= itemCount; i++) {\n            if(withIcon){\n                builder.addItem(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_lab), \"Item \" + i);\n            }else{\n                builder.addItem(\"Item \" + i);\n            }\n\n        }\n        builder.build().show();\n    }\n\n    private void showSimpleBottomSheetGrid() {\n        final int TAG_SHARE_WECHAT_FRIEND = 0;\n        final int TAG_SHARE_WECHAT_MOMENT = 1;\n        final int TAG_SHARE_WEIBO = 2;\n        final int TAG_SHARE_CHAT = 3;\n        final int TAG_SHARE_LOCAL = 4;\n        QMUIBottomSheet.BottomGridSheetBuilder builder = new QMUIBottomSheet.BottomGridSheetBuilder(getActivity());\n        builder.addItem(R.mipmap.icon_more_operation_share_friend, \"分享到微信\", TAG_SHARE_WECHAT_FRIEND, QMUIBottomSheet.BottomGridSheetBuilder.FIRST_LINE)\n                .addItem(R.mipmap.icon_more_operation_share_moment, \"分享到朋友圈\", TAG_SHARE_WECHAT_MOMENT, QMUIBottomSheet.BottomGridSheetBuilder.FIRST_LINE)\n                .addItem(R.mipmap.icon_more_operation_share_weibo, \"分享到微博\", TAG_SHARE_WEIBO, QMUIBottomSheet.BottomGridSheetBuilder.FIRST_LINE)\n                .addItem(R.mipmap.icon_more_operation_share_chat, \"分享到私信\", TAG_SHARE_CHAT, QMUIBottomSheet.BottomGridSheetBuilder.FIRST_LINE)\n                .addItem(R.mipmap.icon_more_operation_save, \"保存到本地\", TAG_SHARE_LOCAL, QMUIBottomSheet.BottomGridSheetBuilder.SECOND_LINE)\n                .setAddCancelBtn(true)\n                .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomGridSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView) {\n                        dialog.dismiss();\n                        int tag = (int) itemView.getTag();\n                        switch (tag) {\n                            case TAG_SHARE_WECHAT_FRIEND:\n                                Toast.makeText(getActivity(), \"分享到微信\", Toast.LENGTH_SHORT).show();\n                                break;\n                            case TAG_SHARE_WECHAT_MOMENT:\n                                Toast.makeText(getActivity(), \"分享到朋友圈\", Toast.LENGTH_SHORT).show();\n                                break;\n                            case TAG_SHARE_WEIBO:\n                                Toast.makeText(getActivity(), \"分享到微博\", Toast.LENGTH_SHORT).show();\n                                break;\n                            case TAG_SHARE_CHAT:\n                                Toast.makeText(getActivity(), \"分享到私信\", Toast.LENGTH_SHORT).show();\n                                break;\n                            case TAG_SHARE_LOCAL:\n                                Toast.makeText(getActivity(), \"保存到本地\", Toast.LENGTH_SHORT).show();\n                                break;\n                        }\n                    }\n                }).build().show();\n\n\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDButtonFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment.components\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmui.arch.effect.MapEffect\nimport com.qmuiteam.qmui.kotlin.onClick\nimport com.qmuiteam.qmui.kotlin.skin\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport com.qmuiteam.qmuidemo.manager.QDDataManager\nimport com.qmuiteam.qmuidemo.model.QDItemDescription\n\n@LatestVisitRecord\n@Widget(name = \"QMUIRoundButton\", iconRes = R.mipmap.icon_grid_button)\nclass QDButtonFragment : BaseFragment() {\n    @BindView(R.id.topbar)\n    internal lateinit var mTopBar: QMUITopBarLayout\n    @BindView(R.id.alpha_button)\n    internal lateinit var alphaButton: QMUIRoundButton\n    @BindView(R.id.test_java_kotlin_skin)\n    internal lateinit var kotlinSkinButton: QMUIRoundButton\n    private lateinit var mQDItemDescription: QDItemDescription\n    override fun onCreateView(): View {\n        val view = LayoutInflater.from(activity).inflate(R.layout.fragment_button, null)\n        ButterKnife.bind(this, view)\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.javaClass)\n        alphaButton.setChangeAlphaWhenPress(true)\n        initTopBar()\n\n        kotlinSkinButton.skin {\n            border(R.attr.app_skin_btn_test_border_single)\n            background(R.attr.app_skin_btn_test_bg_single)\n            textColor(R.attr.app_skin_btn_test_border_single)\n        }\n        return view\n    }\n\n    private fun initTopBar() {\n        mTopBar.addLeftBackImageButton().onClick { popBackStack() }\n        mTopBar.setTitle(mQDItemDescription.name)\n\n\n        notifyEffect(MapEffect(HashMap<String, Any>().apply {\n            put(\"interested_type_key\", 1)\n            put(\"interested_value_key\", \"Did you received the change from other fragment?\")\n        }))\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDCollapsingTopBarLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.animation.ValueAnimator;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.widget.QMUICollapsingTopBarLayout;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-09-02\n */\n\n@Widget(widgetClass = QMUICollapsingTopBarLayout.class, iconRes = R.mipmap.icon_grid_collapse_top_bar)\npublic class QDCollapsingTopBarLayoutFragment extends BaseFragment {\n    private static final String TAG = \"CollapsingTopBarLayout\";\n\n    QDRecyclerViewAdapter mRecyclerViewAdapter;\n    LinearLayoutManager mPagerLayoutManager;\n\n\n    @BindView(R.id.recyclerView) RecyclerView mRecyclerView;\n    @BindView(R.id.collapsing_topbar_layout) QMUICollapsingTopBarLayout mCollapsingTopBarLayout;\n    @BindView(R.id.topbar) QMUITopBar mTopBar;\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_collapsing_topbar_layout, null);\n        ButterKnife.bind(this, rootView);\n        initTopBar();\n        mPagerLayoutManager = new LinearLayoutManager(getContext());\n        mRecyclerView.setLayoutManager(mPagerLayoutManager);\n        mRecyclerViewAdapter = new QDRecyclerViewAdapter();\n        mRecyclerViewAdapter.setItemCount(10);\n        mRecyclerView.setAdapter(mRecyclerViewAdapter);\n\n        mCollapsingTopBarLayout.setScrimUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                Log.i(TAG, \"scrim: \" + animation.getAnimatedValue());\n            }\n        });\n\n        mCollapsingTopBarLayout.addOnOffsetUpdateListener(new QMUICollapsingTopBarLayout.OnOffsetUpdateListener() {\n            @Override\n            public void onOffsetChanged(QMUICollapsingTopBarLayout layout, int offset, float expandFraction) {\n                Log.i(TAG, \"offset = \" + offset + \"; expandFraction = \" + expandFraction);\n            }\n        });\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mCollapsingTopBarLayout.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDEmptyViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.widget.QMUIEmptyView;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIEmptyView} 的使用示例。\n * Created by cgspine on 15/9/14.\n */\n\n@Widget(widgetClass = QMUIEmptyView.class, iconRes = R.mipmap.icon_grid_empty_view)\npublic class QDEmptyViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.emptyView)\n    QMUIEmptyView mEmptyView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_emptyview, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        // 切换其他情况的按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                showBottomSheetList();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                .addItem(getResources().getString(R.string.emptyView_mode_title_double_text))\n                .addItem(getResources().getString(R.string.emptyView_mode_title_single_text))\n                .addItem(getResources().getString(R.string.emptyView_mode_title_loading))\n                .addItem(getResources().getString(R.string.emptyView_mode_title_single_text_and_button))\n                .addItem(getResources().getString(R.string.emptyView_mode_title_double_text_and_button))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        switch (position) {\n                            case 0:\n                                mEmptyView.show(getResources().getString(R.string.emptyView_mode_desc_double), getResources().getString(R.string.emptyView_mode_desc_detail_double));\n                                break;\n                            case 1:\n                                mEmptyView.show(getResources().getString(R.string.emptyView_mode_desc_single), null);\n                                break;\n                            case 2:\n                                mEmptyView.show(true);\n                                break;\n                            case 3:\n                                mEmptyView.show(false, getResources().getString(R.string.emptyView_mode_desc_fail_title), null, getResources().getString(R.string.emptyView_mode_desc_retry), null);\n                                break;\n                            case 4:\n                                mEmptyView.show(false, getResources().getString(R.string.emptyView_mode_desc_fail_title), getResources().getString(R.string.emptyView_mode_desc_fail_desc), getResources().getString(R.string.emptyView_mode_desc_retry), null);\n                                break;\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDFloatLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport androidx.core.content.ContextCompat;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUIFloatLayout;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIFloatLayout.class, iconRes = R.mipmap.icon_grid_float_layout)\npublic class QDFloatLayoutFragment extends BaseFragment {\n    private static final String TAG = \"QDFloatLayoutFragment\";\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.qmuidemo_floatlayout)\n    QMUIFloatLayout mFloatLayout;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_floatlayout, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        for (int i = 0; i < 8; i++) {\n            addItemToFloatLayout(mFloatLayout);\n        }\n        mFloatLayout.setOnLineCountChangeListener(new QMUIFloatLayout.OnLineCountChangeListener() {\n            @Override\n            public void onChange(int oldLineCount, int newLineCount) {\n                Log.i(TAG, \"oldLineCount = \" + oldLineCount + \" ;newLineCount = \" + newLineCount);\n            }\n        });\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheet();\n                    }\n                });\n\n    }\n\n    private void addItemToFloatLayout(QMUIFloatLayout floatLayout) {\n        int currentChildCount = floatLayout.getChildCount();\n\n        TextView textView = new TextView(getActivity());\n        int textViewPadding = QMUIDisplayHelper.dp2px(getContext(), 4);\n        textView.setPadding(textViewPadding, textViewPadding, textViewPadding, textViewPadding);\n        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);\n        textView.setTextColor(ContextCompat.getColor(getContext(), R.color.qmui_config_color_white));\n        textView.setText(String.valueOf(currentChildCount));\n        textView.setBackgroundResource(currentChildCount % 2 == 0 ? R.color.app_color_theme_3 : R.color.app_color_theme_6);\n\n        int textViewSize = QMUIDisplayHelper.dpToPx(60);\n        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(textViewSize, textViewSize);\n        floatLayout.addView(textView, lp);\n    }\n\n    private void removeItemFromFloatLayout(QMUIFloatLayout floatLayout) {\n        if (floatLayout.getChildCount() == 0) {\n            return;\n        }\n        floatLayout.removeView(floatLayout.getChildAt(floatLayout.getChildCount() - 1));\n    }\n\n    private void showBottomSheet() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getContext())\n                .addItem(\"增加一个item\")\n                .addItem(\"减少一个item\")\n                .addItem(\"居左\")\n                .addItem(\"居中\")\n                .addItem(\"居右\")\n                .addItem(\"限制最多显示1行\")\n                .addItem(\"限制最多显示4个item\")\n                .addItem(\"不限制行数或个数\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        switch (position) {\n                            case 0:\n                                addItemToFloatLayout(mFloatLayout);\n                                break;\n                            case 1:\n                                removeItemFromFloatLayout(mFloatLayout);\n                                break;\n                            case 2:\n                                mFloatLayout.setGravity(Gravity.LEFT);\n                                break;\n                            case 3:\n                                mFloatLayout.setGravity(Gravity.CENTER_HORIZONTAL);\n                                break;\n                            case 4:\n                                mFloatLayout.setGravity(Gravity.RIGHT);\n                                break;\n                            case 5:\n                                mFloatLayout.setMaxLines(1);\n                                break;\n                            case 6:\n                                mFloatLayout.setMaxNumber(4);\n                                break;\n                            case 7:\n                                mFloatLayout.setMaxLines(Integer.MAX_VALUE);\n                                break;\n                            default:\n                                break;\n                        }\n                        dialog.dismiss();\n                    }\n                })\n                .build().show();\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDGroupListViewFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment.components\n\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Toast\nimport androidx.core.content.ContextCompat\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport com.qmuiteam.qmui.exposure.simpleExposure\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper\nimport com.qmuiteam.qmui.util.QMUIResHelper\nimport com.qmuiteam.qmui.widget.QMUILoadingView\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView\nimport com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView.SkinConfig\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport com.qmuiteam.qmuidemo.manager.QDDataManager\nimport com.qmuiteam.qmuidemo.model.QDItemDescription\n\n/**\n * [QMUIGroupListView] 的使用示例。\n * Created by Kayo on 2016/11/21.\n */\n@Widget(widgetClass = QMUIGroupListView::class, iconRes = R.mipmap.icon_grid_group_list_view)\nclass QDGroupListViewFragment : BaseFragment() {\n    @JvmField\n    @BindView(R.id.topbar)\n    var mTopBar: QMUITopBarLayout? = null\n\n    @JvmField\n    @BindView(R.id.groupListView)\n    var mGroupListView: QMUIGroupListView? = null\n    private var mQDItemDescription: QDItemDescription? = null\n    override fun onCreateView(): View {\n        val root = LayoutInflater.from(activity).inflate(R.layout.fragment_grouplistview, null)\n        ButterKnife.bind(this, root)\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.javaClass)\n        initTopBar()\n        initGroupListView()\n        return root\n    }\n\n    private fun initTopBar() {\n        mTopBar!!.addLeftBackImageButton().setOnClickListener { popBackStack() }\n        mTopBar!!.setTitle(mQDItemDescription!!.name)\n    }\n\n    private fun initGroupListView() {\n        val normalItem = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"Item 1\",\n            null,\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_NONE\n        )\n        normalItem.orientation = QMUICommonListItemView.VERTICAL\n        val itemWithDetail = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.example_image0),\n            \"Item 2\",\n            null,\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_NONE\n        )\n\n        // 去除 icon 的 tintColor 换肤设置\n        val skinConfig = SkinConfig()\n        skinConfig.iconTintColorRes = 0\n        itemWithDetail.setSkinConfig(skinConfig)\n        itemWithDetail.detailText = \"在右方的详细信息\"\n        val itemWithDetailBelow = mGroupListView!!.createItemView(\"Item 3\")\n        itemWithDetailBelow.simpleExposure(key = \"\") { type ->\n            Log.i(\"exposure\", \"simple exposure: $type\")\n        }\n        itemWithDetailBelow.orientation = QMUICommonListItemView.VERTICAL\n        itemWithDetailBelow.detailText = \"在标题下方的详细信息\"\n        val itemWithChevron = mGroupListView!!.createItemView(\"Item 4\")\n        itemWithChevron.accessoryType = QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON\n        val itemWithSwitch = mGroupListView!!.createItemView(\"Item 5\")\n        itemWithSwitch.accessoryType = QMUICommonListItemView.ACCESSORY_TYPE_SWITCH\n        itemWithSwitch.switch.setOnCheckedChangeListener { _, isChecked ->\n            Toast.makeText(\n                activity,\n                \"checked = $isChecked\",\n                Toast.LENGTH_SHORT\n            ).show()\n        }\n        val itemWithDetailBelowWithChevron = mGroupListView!!.createItemView(\"Item 6\")\n        itemWithDetailBelowWithChevron.orientation = QMUICommonListItemView.VERTICAL\n        itemWithDetailBelowWithChevron.accessoryType = QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON\n        itemWithDetailBelowWithChevron.detailText = \"在标题下方的详细信息\"\n        val longTitleAndDetail = mGroupListView!!.createItemView(\n            null,\n            \"标题有点长；标题有点长；标题有点长；标题有点长；标题有点长；标题有点长\",\n            \"详细信息有点长; 详细信息有点长；详细信息有点长；详细信息有点长;详细信息有点长\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            ViewGroup.LayoutParams.WRAP_CONTENT\n        )\n        val paddingVer = QMUIDisplayHelper.dp2px(context, 12)\n        longTitleAndDetail.setPadding(\n            longTitleAndDetail.paddingLeft, paddingVer,\n            longTitleAndDetail.paddingRight, paddingVer\n        )\n        val height = QMUIResHelper.getAttrDimen(context, com.qmuiteam.qmui.R.attr.qmui_list_item_height)\n        val itemWithDetailBelowWithChevronWithIcon = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"Item 7\",\n            \"在标题下方的详细信息\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        val itemWithCustom = mGroupListView!!.createItemView(\"右方自定义 View\")\n        itemWithCustom.accessoryType = QMUICommonListItemView.ACCESSORY_TYPE_CUSTOM\n        val loadingView = QMUILoadingView(activity)\n        itemWithCustom.addAccessoryCustomView(loadingView)\n        val itemRedPoint1 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"红点显示在左边\",\n            \"在标题下方的详细信息\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemRedPoint1.setTipPosition(QMUICommonListItemView.TIP_POSITION_LEFT)\n        itemRedPoint1.showRedDot(true)\n        val itemRedPoint2 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"红点显示在右边\",\n            \"在标题下方的详细信息\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemRedPoint2.setTipPosition(QMUICommonListItemView.TIP_POSITION_RIGHT)\n        itemRedPoint2.showRedDot(true)\n        val itemRedPoint3 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"红点显示在左边\",\n            \"在右方的详细信息\",\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemRedPoint3.setTipPosition(QMUICommonListItemView.TIP_POSITION_LEFT)\n        itemRedPoint3.showRedDot(true)\n        val itemRedPoint4 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"红点显示在右边\",\n            \"在右方的详细信息\",\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemRedPoint4.setTipPosition(QMUICommonListItemView.TIP_POSITION_RIGHT)\n        itemRedPoint4.showRedDot(true)\n        val itemNew1 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"new 标识显示在左边\",\n            \"在标题下方的详细信息\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemNew1.setTipPosition(QMUICommonListItemView.TIP_POSITION_LEFT)\n        itemNew1.showNewTip(true)\n        val itemNew2 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"new 标识显示在右边\",\n            \"在标题下方的详细信息\",\n            QMUICommonListItemView.VERTICAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemNew2.setTipPosition(QMUICommonListItemView.TIP_POSITION_RIGHT)\n        itemNew2.showNewTip(true)\n        val itemNew3 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"new 标识显示在左边\",\n            \"在右方的详细信息\",\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemNew3.setTipPosition(QMUICommonListItemView.TIP_POSITION_LEFT)\n        itemNew3.showNewTip(true)\n        val itemNew4 = mGroupListView!!.createItemView(\n            ContextCompat.getDrawable(requireContext(), R.mipmap.about_logo),\n            \"new 标识显示在右边\",\n            \"在右方的详细信息\",\n            QMUICommonListItemView.HORIZONTAL,\n            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,\n            height\n        )\n        itemNew4.setTipPosition(QMUICommonListItemView.TIP_POSITION_RIGHT)\n        itemNew4.showNewTip(true)\n        val onClickListener = View.OnClickListener { v ->\n            if (v is QMUICommonListItemView) {\n                val text = v.text\n                Toast.makeText(activity, \"$text is Clicked\", Toast.LENGTH_SHORT).show()\n                if (v.accessoryType == QMUICommonListItemView.ACCESSORY_TYPE_SWITCH) {\n                    v.switch.toggle()\n                }\n            }\n        }\n        val size = QMUIDisplayHelper.dp2px(context, 20)\n        QMUIGroupListView.newSection(context)\n            .setTitle(\"Section 1: 默认提供的样式\")\n            .setDescription(\"Section 1 的描述\")\n            .setLeftIconSize(size, ViewGroup.LayoutParams.WRAP_CONTENT)\n            .addItemView(normalItem, onClickListener)\n            .addItemView(itemWithDetail, onClickListener)\n            .addItemView(itemWithDetailBelow, onClickListener)\n            .addItemView(itemWithChevron, onClickListener)\n            .addItemView(itemWithSwitch, onClickListener)\n            .addItemView(itemWithDetailBelowWithChevron, onClickListener)\n            .addItemView(itemWithDetailBelowWithChevronWithIcon, onClickListener)\n            .addItemView(longTitleAndDetail, onClickListener)\n            .setMiddleSeparatorInset(QMUIDisplayHelper.dp2px(context, 16), 0)\n            .addTo(mGroupListView)\n        QMUIGroupListView.newSection(context)\n            .setTitle(\"Section 2: 自定义右侧 View/红点/new 提示\")\n            .setLeftIconSize(size, ViewGroup.LayoutParams.WRAP_CONTENT)\n            .addItemView(itemWithCustom, onClickListener)\n            .addItemView(itemRedPoint1, onClickListener)\n            .addItemView(itemRedPoint2, onClickListener)\n            .addItemView(itemRedPoint3, onClickListener)\n            .addItemView(itemRedPoint4, onClickListener)\n            .addItemView(itemNew1, onClickListener)\n            .addItemView(itemNew2, onClickListener)\n            .addItemView(itemNew3, onClickListener)\n            .addItemView(itemNew4, onClickListener)\n            .setOnlyShowStartEndSeparator(true)\n            .addTo(mGroupListView)\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.RadioGroup;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.layout.QMUILayoutHelper;\nimport com.qmuiteam.qmui.layout.QMUILinearLayout;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\n\n/**\n * Created by cgspine on 2018/3/22.\n */\n\n@Widget(name = \"QMUILayout\", iconRes = R.mipmap.icon_grid_layout)\npublic class QDLayoutFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.layout_for_test) QMUILinearLayout mTestLayout;\n    @BindView(R.id.test_seekbar_alpha) SeekBar mAlphaSeekBar;\n    @BindView(R.id.test_seekbar_elevation) SeekBar mElevationSeekBar;\n    @BindView(R.id.alpha_tv) TextView mAlphaTv;\n    @BindView(R.id.elevation_tv) TextView mElevationTv;\n    @BindView(R.id.hide_radius_group) RadioGroup mHideRadiusGroup;\n\n    private QDItemDescription mQDItemDescription;\n    private float mShadowAlpha = 0.25f;\n    private int mShadowElevationDp = 14;\n    private int mRadius;\n\n    @OnClick(R.id.shadow_color_red)\n    void changeToShadowColorRed(){\n        mTestLayout.setShadowColor(0xffff0000);\n    }\n\n    @OnClick(R.id.shadow_color_blue)\n    void changeToShadowColorBlue(){\n        mTestLayout.setShadowColor(0xff0000ff);\n    }\n\n    @OnClick(R.id.radius_15dp)\n    void changeToRadius15dp(){\n        mRadius = QMUIDisplayHelper.dp2px(getContext(), 15);\n        mTestLayout.setRadius(mRadius);\n    }\n\n    @OnClick(R.id.radius_half_width)\n    void changeToRadiusHalfWidth(){\n        mRadius = QMUILayoutHelper.RADIUS_OF_HALF_VIEW_WIDTH;\n        mTestLayout.setRadius(mRadius);\n    }\n\n    @OnClick(R.id.radius_half_height)\n    void changeToRadiusHalfHeight(){\n        mRadius = QMUILayoutHelper.RADIUS_OF_HALF_VIEW_HEIGHT;\n        mTestLayout.setRadius(mRadius);\n    }\n\n    @Override\n    protected View onCreateView() {\n        mRadius = QMUIDisplayHelper.dp2px(getContext(), 15);\n        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_layout, null);\n        ButterKnife.bind(this, view);\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initLayout();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initLayout() {\n        mAlphaSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n            @Override\n            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n                mShadowAlpha = progress * 1f / 100;\n                mAlphaTv.setText(\"alpha: \" + mShadowAlpha);\n                mTestLayout.setRadiusAndShadow(mRadius,\n                        QMUIDisplayHelper.dp2px(getContext(), mShadowElevationDp),\n                        mShadowAlpha);\n            }\n\n            @Override\n            public void onStartTrackingTouch(SeekBar seekBar) {\n\n            }\n\n            @Override\n            public void onStopTrackingTouch(SeekBar seekBar) {\n\n            }\n        });\n\n        mElevationSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n            @Override\n            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n                mShadowElevationDp = progress;\n                mElevationTv.setText(\"elevation: \" + progress + \"dp\");\n                mTestLayout.setRadiusAndShadow(mRadius,\n                        QMUIDisplayHelper.dp2px(getActivity(), mShadowElevationDp), mShadowAlpha);\n            }\n\n            @Override\n            public void onStartTrackingTouch(SeekBar seekBar) {\n\n            }\n\n            @Override\n            public void onStopTrackingTouch(SeekBar seekBar) {\n\n            }\n        });\n\n        mAlphaSeekBar.setProgress((int) (mShadowAlpha * 100));\n        mElevationSeekBar.setProgress(mShadowElevationDp);\n\n        mHideRadiusGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(RadioGroup group, int checkedId) {\n                switch (checkedId) {\n                    case R.id.hide_radius_none:\n                        mTestLayout.setRadius(mRadius, QMUILayoutHelper.HIDE_RADIUS_SIDE_NONE);\n                        break;\n                    case R.id.hide_radius_left:\n                        mTestLayout.setRadius(mRadius, QMUILayoutHelper.HIDE_RADIUS_SIDE_LEFT);\n                        break;\n                    case R.id.hide_radius_top:\n                        mTestLayout.setRadius(mRadius, QMUILayoutHelper.HIDE_RADIUS_SIDE_TOP);\n                        break;\n                    case R.id.hide_radius_bottom:\n                        mTestLayout.setRadius(mRadius, QMUILayoutHelper.HIDE_RADIUS_SIDE_BOTTOM);\n                        break;\n                    case R.id.hide_radius_right:\n                        mTestLayout.setRadius(mRadius, QMUILayoutHelper.HIDE_RADIUS_SIDE_RIGHT);\n                        break;\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDLinkTextViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.textview.QMUILinkTextView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-05-05\n */\n\n@Widget(widgetClass = QMUILinkTextView.class, iconRes = R.mipmap.icon_grid_link_text_view)\npublic class QDLinkTextViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.link_text_view) QMUILinkTextView mLinkTextView;\n\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_link_texview_layout, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n        mLinkTextView.setOnLinkClickListener(mOnLinkClickListener);\n        mLinkTextView.setOnLinkLongClickListener(new QMUILinkTextView.OnLinkLongClickListener() {\n            @Override\n            public void onLongClick(String text) {\n                Toast.makeText(getContext(), \"long click: \" + text, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mLinkTextView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Toast.makeText(getContext(), \"click TextView\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        // if parent click event should be triggered when TextView area is clicked\n//        mLinkTextView.setNeedForceEventToParent(true);\n//        view.setOnClickListener(new View.OnClickListener() {\n//            @Override\n//            public void onClick(View v) {\n//                Toast.makeText(getContext(), \"forceEventToParent\", Toast.LENGTH_SHORT).show();\n//            }\n//        });\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n    }\n\n    private QMUILinkTextView.OnLinkClickListener mOnLinkClickListener = new QMUILinkTextView.OnLinkClickListener() {\n        @Override\n        public void onTelLinkClick(String phoneNumber) {\n            Toast.makeText(getContext(), \"识别到电话号码是：\" + phoneNumber, Toast.LENGTH_SHORT).show();\n        }\n\n        @Override\n        public void onMailLinkClick(String mailAddress) {\n            Toast.makeText(getContext(), \"识别到邮件地址是：\" + mailAddress, Toast.LENGTH_SHORT).show();\n        }\n\n        @Override\n        public void onWebUrlLinkClick(String url) {\n            Toast.makeText(getContext(), \"识别到网页链接是：\" + url, Toast.LENGTH_SHORT).show();\n        }\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDPopupFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.EditText;\nimport android.widget.FrameLayout;\nimport android.widget.PopupWindow;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIKeyboardHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.popup.QMUIFullScreenPopup;\nimport com.qmuiteam.qmui.widget.popup.QMUIPopup;\nimport com.qmuiteam.qmui.widget.popup.QMUIPopups;\nimport com.qmuiteam.qmui.widget.popup.QMUIQuickAction;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\n\n/**\n * @author cginechen\n * @date 2017-03-27\n */\n\n@Widget(widgetClass = QMUIPopups.class, iconRes = R.mipmap.icon_grid_popup)\n@LatestVisitRecord\npublic class QDPopupFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n\n    private QMUIPopup mNormalPopup;\n\n    @OnClick(R.id.actionBtn1)\n    void onClickBtn1(View v) {\n        TextView textView = new TextView(getContext());\n        textView.setLineSpacing(QMUIDisplayHelper.dp2px(getContext(), 4), 1.0f);\n        int padding = QMUIDisplayHelper.dp2px(getContext(), 20);\n        textView.setPadding(padding, padding, padding, padding);\n        textView.setText(\"QMUIBasePopup 可以设置其位置以及显示和隐藏的动画\");\n        textView.setTextColor(\n                QMUIResHelper.getAttrColor(getContext(), R.attr.app_skin_common_title_text_color));\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.textColor(R.attr.app_skin_common_title_text_color);\n        QMUISkinHelper.setSkinValue(textView, builder);\n        builder.release();\n        mNormalPopup = QMUIPopups.popup(getContext(), QMUIDisplayHelper.dp2px(getContext(), 250))\n                .preferredDirection(QMUIPopup.DIRECTION_BOTTOM)\n                .view(textView)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .offsetX(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .offsetYIfBottom(QMUIDisplayHelper.dp2px(getContext(), 5))\n                .shadow(true)\n                .arrow(true)\n                .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn2)\n    void onClickBtn2(View v) {\n        String[] listItems = new String[]{\n                \"Item 1\",\n                \"Item 2\",\n                \"Item 3\",\n                \"Item 4\",\n                \"Item 5\",\n                \"Item 6\",\n                \"Item 7\",\n                \"Item 8\",\n        };\n        List<String> data = new ArrayList<>();\n\n        Collections.addAll(data, listItems);\n\n        ArrayAdapter adapter = new ArrayAdapter<>(getContext(), R.layout.simple_list_item, data);\n        AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {\n                Toast.makeText(getActivity(), \"Item \" + (i + 1), Toast.LENGTH_SHORT).show();\n                if (mNormalPopup != null) {\n                    mNormalPopup.dismiss();\n                }\n            }\n        };\n        mNormalPopup = QMUIPopups.listPopup(getContext(),\n                QMUIDisplayHelper.dp2px(getContext(), 250),\n                QMUIDisplayHelper.dp2px(getContext(), 300),\n                adapter,\n                onItemClickListener)\n                .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)\n                .preferredDirection(QMUIPopup.DIRECTION_TOP)\n                .shadow(true)\n                .offsetYIfTop(QMUIDisplayHelper.dp2px(getContext(), 5))\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn3)\n    void onClickBtn3(View v) {\n        TextView textView = new TextView(getContext());\n        textView.setLineSpacing(QMUIDisplayHelper.dp2px(getContext(), 4), 1.0f);\n        int padding = QMUIDisplayHelper.dp2px(getContext(), 20);\n        textView.setPadding(padding, padding, padding, padding);\n        textView.setText(\"通过 dimAmount() 设置背景遮罩\");\n        textView.setTextColor(\n                QMUIResHelper.getAttrColor(getContext(), R.attr.app_skin_common_title_text_color));\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        builder.textColor(R.attr.app_skin_common_title_text_color);\n        QMUISkinHelper.setSkinValue(textView, builder);\n        builder.release();\n        mNormalPopup = QMUIPopups.popup(getContext(), QMUIDisplayHelper.dp2px(getContext(), 250))\n                .preferredDirection(QMUIPopup.DIRECTION_BOTTOM)\n                .view(textView)\n                .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .dimAmount(0.6f)\n                .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn4)\n    void onClickBtn4(View v) {\n        final TextView textView = new TextView(getContext());\n        textView.setLineSpacing(QMUIDisplayHelper.dp2px(getContext(), 4), 1.0f);\n        int padding = QMUIDisplayHelper.dp2px(getContext(), 20);\n        textView.setPadding(padding, padding, padding, padding);\n        textView.setText(\"加载中...\");\n        textView.setTextColor(ContextCompat.getColor(getContext(), R.color.app_color_description));\n        mNormalPopup = QMUIPopups.popup(getContext(), QMUIDisplayHelper.dp2px(getContext(), 250))\n                .preferredDirection(QMUIPopup.DIRECTION_BOTTOM)\n                .view(textView)\n                .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .dimAmount(0.6f)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n\n        // 这里只是演示，实际情况应该考虑数据加载完成而 Popup 被 dismiss 的情况\n        textView.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                textView.setText(\"使用 Popup 最好是一开始就确定内容宽高，\" +\n                        \"如果宽高位置会变化，系统会有一个的移动动画不受控制，体验并不好\");\n            }\n        }, 2000);\n    }\n\n    @OnClick(R.id.actionBtn5)\n    void onClickBtn5(View v) {\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        QMUIFrameLayout frameLayout = new QMUIFrameLayout(getContext());\n        frameLayout.setBackground(\n                QMUIResHelper.getAttrDrawable(getContext(), R.attr.qmui_skin_support_popup_bg));\n        builder.background(R.attr.qmui_skin_support_popup_bg);\n        QMUISkinHelper.setSkinValue(frameLayout, builder);\n        frameLayout.setRadius(QMUIDisplayHelper.dp2px(getContext(), 12));\n        int padding = QMUIDisplayHelper.dp2px(getContext(), 20);\n        frameLayout.setPadding(padding, padding, padding, padding);\n\n        TextView textView = new TextView(getContext());\n        textView.setLineSpacing(QMUIDisplayHelper.dp2px(getContext(), 4), 1.0f);\n        textView.setPadding(padding, padding, padding, padding);\n        textView.setText(\"这是自定义显示的内容\");\n        textView.setTextColor(\n                QMUIResHelper.getAttrColor(getContext(), R.attr.app_skin_common_title_text_color));\n\n        builder.clear();\n        builder.textColor(R.attr.app_skin_common_title_text_color);\n        QMUISkinHelper.setSkinValue(textView, builder);\n        textView.setGravity(Gravity.CENTER);\n\n        builder.release();\n\n        int size = QMUIDisplayHelper.dp2px(getContext(), 200);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size, size);\n        frameLayout.addView(textView, lp);\n\n        QMUIPopups.fullScreenPopup(getContext())\n                .addView(frameLayout)\n                .closeBtn(true)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .onBlankClick(new QMUIFullScreenPopup.OnBlankClickListener() {\n                    @Override\n                    public void onBlankClick(QMUIFullScreenPopup popup) {\n                        Toast.makeText(getContext(), \"点击到空白区域\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn6)\n    void onClickBtn6(View v) {\n        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n        QMUIFrameLayout frameLayout = new QMUIFrameLayout(getContext());\n        frameLayout.setBackground(\n                QMUIResHelper.getAttrDrawable(getContext(), R.attr.qmui_skin_support_popup_bg));\n        builder.background(R.attr.qmui_skin_support_popup_bg);\n        QMUISkinHelper.setSkinValue(frameLayout, builder);\n        frameLayout.setRadius(QMUIDisplayHelper.dp2px(getContext(), 12));\n        int padding = QMUIDisplayHelper.dp2px(getContext(), 20);\n        frameLayout.setPadding(padding, padding, padding, padding);\n        QMUIKeyboardHelper.listenKeyBoardWithOffsetSelfHalf(frameLayout, true);\n\n        TextView textView = new TextView(getContext());\n        textView.setLineSpacing(QMUIDisplayHelper.dp2px(getContext(), 4), 1.0f);\n        textView.setPadding(padding, padding, padding, padding);\n        textView.setText(\"这是自定义显示的内容\");\n        builder.clear();\n        builder.textColor(R.attr.app_skin_common_title_text_color);\n        QMUISkinHelper.setSkinValue(textView, builder);\n        textView.setGravity(Gravity.CENTER);\n        int size = QMUIDisplayHelper.dp2px(getContext(), 200);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size, size);\n        frameLayout.addView(textView, lp);\n\n        final FrameLayout editFitSystemWindowWrapped = new FrameLayout(getContext());\n        editFitSystemWindowWrapped.setFitsSystemWindows(true);\n        QMUIWindowInsetHelper.handleWindowInsets(editFitSystemWindowWrapped,\n                WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.displayCutout(), true);\n        QMUIKeyboardHelper.listenKeyBoardWithOffsetSelf(editFitSystemWindowWrapped, true);\n\n        int minHeight = QMUIDisplayHelper.dp2px(getContext(), 48);\n        QMUIFrameLayout editParent = new QMUIFrameLayout(getContext());\n        editParent.setMinimumHeight(minHeight);\n        editParent.setRadius(minHeight / 2);\n        editParent.setBackground(\n                QMUIResHelper.getAttrDrawable(getContext(), R.attr.qmui_skin_support_popup_bg));\n        builder.clear();\n        builder.background(R.attr.qmui_skin_support_popup_bg);\n        QMUISkinHelper.setSkinValue(editParent, builder);\n\n\n        EditText editText = new EditText(getContext());\n        editText.setHint(\"请输入...\");\n        editText.setBackground(null);\n        builder.clear();\n        builder.hintColor(R.attr.app_skin_common_desc_text_color);\n        builder.textColor(R.attr.app_skin_common_title_text_color);\n        QMUISkinHelper.setSkinValue(editText, builder);\n        int paddingHor = QMUIDisplayHelper.dp2px(getContext(), 20);\n        int paddingVer = QMUIDisplayHelper.dp2px(getContext(), 10);\n        editText.setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n        editText.setMaxHeight(QMUIDisplayHelper.dp2px(getContext(), 100));\n\n        FrameLayout.LayoutParams editLp = new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        editLp.gravity = Gravity.CENTER_HORIZONTAL;\n        editParent.addView(editText, editLp);\n        editFitSystemWindowWrapped.addView(editParent,  new FrameLayout.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n\n        ConstraintLayout.LayoutParams eLp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);\n        int mar = QMUIDisplayHelper.dp2px(getContext(), 20);\n        eLp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;\n        eLp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;\n        eLp.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n        eLp.leftMargin = mar;\n        eLp.rightMargin = mar;\n        eLp.bottomMargin = mar;\n\n        QMUIPopups.fullScreenPopup(getContext())\n                .addView(frameLayout)\n                .addView(editFitSystemWindowWrapped, eLp)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .onBlankClick(new QMUIFullScreenPopup.OnBlankClickListener() {\n                    @Override\n                    public void onBlankClick(QMUIFullScreenPopup popup) {\n                        popup.dismiss();\n                        Toast.makeText(getContext(), \"点击到空白区域\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .onDismiss(new PopupWindow.OnDismissListener() {\n                    @Override\n                    public void onDismiss() {\n                        Toast.makeText(getContext(), \"onDismiss\", Toast.LENGTH_SHORT).show();\n                    }\n                })\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn7)\n    void onClickBtn7(View v) {\n        QMUIPopups.quickAction(getContext(),\n                QMUIDisplayHelper.dp2px(getContext(), 56),\n                QMUIDisplayHelper.dp2px(getContext(), 56))\n                .shadow(true)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_copy).text(\"复制\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"复制成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_line).text(\"划线\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"划线成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_share).text(\"分享\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"分享成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .show(v);\n    }\n\n    @OnClick(R.id.actionBtn8)\n    void onClickBtn8(View v) {\n        QMUIPopups.quickAction(getContext(),\n                QMUIDisplayHelper.dp2px(getContext(), 56),\n                QMUIDisplayHelper.dp2px(getContext(), 56))\n                .shadow(true)\n                .skinManager(QMUISkinManager.defaultInstance(getContext()))\n                .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_copy).text(\"复制\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"复制成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_line).text(\"划线\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"划线成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_share).text(\"分享\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"分享成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_delete_line).text(\"删除划线\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"删除划线成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_dict).text(\"词典\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"打开词典\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_share).text(\"圈圈\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"查询成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .addAction(new QMUIQuickAction.Action().icon(R.drawable.icon_quick_action_dict).text(\"查询\").onClick(\n                        new QMUIQuickAction.OnClickListener() {\n                            @Override\n                            public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) {\n                                quickAction.dismiss();\n                                Toast.makeText(getContext(), \"查询成功\", Toast.LENGTH_SHORT).show();\n                            }\n                        }\n                ))\n                .show(v);\n    }\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_popup, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n        return root;\n    }\n\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDPriorityLinearLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIPriorityLinearLayout.class, iconRes = R.mipmap.icon_grid_float_layout)\npublic class QDPriorityLinearLayoutFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n\n    public QDPriorityLinearLayoutFragment() {\n    }\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getContext()).inflate(\n                R.layout.fragment_priority_linear_layout, null);\n        ButterKnife.bind(this, rootView);\n        initTopBar();\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n}\n\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDProgressBarFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.os.Handler;\nimport android.os.Message;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUIProgressBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.lang.ref.WeakReference;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIProgressBar} 的使用示例。\n * Created by cgspine on 15/9/15.\n */\n@Widget(widgetClass = QMUIProgressBar.class, iconRes = R.mipmap.icon_grid_progress_bar)\npublic class QDProgressBarFragment extends BaseFragment {\n\n    protected static final int STOP = 0x10000;\n    protected static final int NEXT = 0x10001;\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.rectProgressBar)\n    QMUIProgressBar mRectProgressBar;\n    @BindView(R.id.circleProgressBar)\n    QMUIProgressBar mCircleProgressBar;\n    @BindView(R.id.startBtn)\n    QMUIRoundButton mStartBtn;\n    @BindView(R.id.backBtn)\n    QMUIRoundButton mBackBtn;\n    int count;\n\n    private QDItemDescription mQDItemDescription;\n    private ProgressHandler myHandler = new ProgressHandler();\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_progressbar, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        mRectProgressBar.setQMUIProgressBarTextGenerator(new QMUIProgressBar.QMUIProgressBarTextGenerator() {\n            @Override\n            public String generateText(QMUIProgressBar progressBar, int value, int maxValue) {\n                return value + \"/\" + maxValue;\n            }\n        });\n\n        mCircleProgressBar.setQMUIProgressBarTextGenerator(new QMUIProgressBar.QMUIProgressBarTextGenerator() {\n            @Override\n            public String generateText(QMUIProgressBar progressBar, int value, int maxValue) {\n                return 100 * value / maxValue + \"%\";\n            }\n        });\n\n        myHandler.setProgressBar(mRectProgressBar, mCircleProgressBar);\n\n        mStartBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                count = 0;\n                new Thread(new Runnable() {\n                    @Override\n                    public void run() {\n                        for (int i = 0; i <= 100; i++) {\n                            try {\n                                count = i + 1;\n                                if (i == 5) {\n                                    Message msg = new Message();\n                                    msg.what = STOP;\n                                    myHandler.sendMessage(msg);\n                                } else {\n                                    Message msg = new Message();\n                                    msg.what = NEXT;\n                                    msg.arg1 = count;\n                                    myHandler.sendMessage(msg);\n                                }\n\n                            } catch (Exception e) {\n                                e.printStackTrace();\n                            }\n                        }\n                    }\n                }).start();\n            }\n        });\n\n        mBackBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mRectProgressBar.setProgress(0);\n                mCircleProgressBar.setProgress(0);\n            }\n        });\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private static class ProgressHandler extends Handler {\n        private WeakReference<QMUIProgressBar> weakRectProgressBar;\n        private WeakReference<QMUIProgressBar> weakCircleProgressBar;\n\n        void setProgressBar(QMUIProgressBar rectProgressBar, QMUIProgressBar circleProgressBar) {\n            weakRectProgressBar = new WeakReference<>(rectProgressBar);\n            weakCircleProgressBar = new WeakReference<>(circleProgressBar);\n        }\n\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            switch (msg.what) {\n                case STOP:\n                    break;\n                case NEXT:\n                    if (!Thread.currentThread().isInterrupted()) {\n                        if (weakRectProgressBar.get() != null && weakCircleProgressBar.get() != null) {\n                            weakRectProgressBar.get().setProgress(msg.arg1);\n                            weakCircleProgressBar.get().setProgress(msg.arg1);\n                        }\n                    }\n            }\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDPullRefreshFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment.components\n\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Toast\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport com.qmuiteam.qmui.exposure.Exposure\nimport com.qmuiteam.qmui.exposure.ExposureType\nimport com.qmuiteam.qmui.exposure.bindExposure\nimport com.qmuiteam.qmui.exposure.registerExposure\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet.BottomListSheetBuilder\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUICenterGravityRefreshOffsetCalculator\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIDefaultRefreshOffsetCalculator\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIFollowRefreshOffsetCalculator\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout.OnPullListener\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport com.qmuiteam.qmuidemo.manager.QDDataManager\nimport com.qmuiteam.qmuidemo.model.QDItemDescription\nimport java.util.*\n\nclass ListItemExposure(val text: String): Exposure {\n    override fun same(data: Exposure): Boolean {\n        return data is ListItemExposure && data.text == text\n    }\n\n    override fun expose(view: View, type: ExposureType) {\n        Log.i(\"exposure\", \"list: $text; $text\")\n    }\n\n    override fun toString(): String {\n        return \"ListItemExposure: $text\"\n    }\n}\n\n/**\n * @author cginechen\n * @date 2016-12-14\n */\n@Widget(widgetClass = QMUIPullRefreshLayout::class, iconRes = R.mipmap.icon_grid_pull_refresh_layout)\nclass QDPullRefreshFragment : BaseFragment() {\n    @JvmField\n    @BindView(R.id.topbar)\n    var mTopBar: QMUITopBarLayout? = null\n\n    @JvmField\n    @BindView(R.id.pull_to_refresh)\n    var mPullRefreshLayout: QMUIPullRefreshLayout? = null\n\n    @JvmField\n    @BindView(R.id.listview)\n    var mListView: RecyclerView? = null\n    private var mAdapter: BaseRecyclerAdapter<String>? = null\n    private var mQDItemDescription: QDItemDescription? = null\n    override fun onCreateView(): View {\n        val root = LayoutInflater.from(activity).inflate(R.layout.fragment_pull_refresh_listview, null)\n        ButterKnife.bind(this, root)\n        val QDDataManager = QDDataManager.getInstance()\n        mQDItemDescription = QDDataManager.getDescription(this.javaClass)\n        initTopBar()\n        initData()\n        return root\n    }\n\n    private fun initTopBar() {\n        mTopBar!!.addLeftBackImageButton().setOnClickListener { popBackStack() }\n        mTopBar!!.setTitle(mQDItemDescription!!.name)\n\n        // 切换其他情况的按钮\n        mTopBar!!.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button).setOnClickListener { showBottomSheetList() }\n    }\n\n    private fun initData() {\n        mListView!!.layoutManager = object : LinearLayoutManager(context) {\n            override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {\n                return RecyclerView.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT,\n                    ViewGroup.LayoutParams.WRAP_CONTENT\n                )\n            }\n        }\n        mAdapter = object : BaseRecyclerAdapter<String>(context, null) {\n            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {\n                return super.onCreateViewHolder(parent, viewType).apply {\n                    itemView.registerExposure()\n                }\n            }\n\n            override fun getItemLayoutId(viewType: Int): Int {\n                return android.R.layout.simple_list_item_1\n            }\n\n\n            override fun bindData(holder: RecyclerViewHolder, position: Int, item: String) {\n                holder.setText(android.R.id.text1, item)\n                holder.itemView.bindExposure(ListItemExposure(item))\n            }\n        }\n        mAdapter?.setOnItemClickListener(BaseRecyclerAdapter.OnItemClickListener { _, pos ->\n            Toast.makeText(\n                context,\n                \"click position=$pos\",\n                Toast.LENGTH_SHORT\n            ).show()\n        })\n        mListView!!.adapter = mAdapter\n        onDataLoaded()\n        mPullRefreshLayout!!.setOnPullListener(object : OnPullListener {\n            override fun onMoveTarget(offset: Int) {}\n            override fun onMoveRefreshView(offset: Int) {}\n            override fun onRefresh() {\n                mPullRefreshLayout!!.postDelayed({\n                    onDataLoaded()\n                    // for test exposure\n                    count++\n                    val data = when (count) {\n                        1 -> {\n                            listOf(\n                                \"Maintain\", \"Helps\",  \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"\n                            )\n                        }\n                        2 -> {\n                            listOf(\n                                \"hehe\",\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"\n                            )\n                        }\n                        else -> {\n                            listOf(\n                                \"xixi\",\"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"\n                            )\n                        }\n                    }\n                    mAdapter!!.setData(data)\n                    mPullRefreshLayout!!.finishRefresh()\n                }, 2000)\n            }\n        })\n    }\n\n    private fun onDataLoaded() {\n        val data = listOf(\n            \"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n            \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"\n        )\n        mAdapter!!.setData(data)\n    }\n\n    private var count = 0\n\n    private fun showBottomSheetList() {\n        BottomListSheetBuilder(activity)\n            .addItem(resources.getString(R.string.pull_refresh_default_offset_calculator))\n            .addItem(resources.getString(R.string.pull_refresh_follow_offset_calculator))\n            .addItem(resources.getString(R.string.pull_refresh_center_gravity_offset_calculator))\n            .setOnSheetItemClickListener { dialog, _, position, _ ->\n                dialog.dismiss()\n                when (position) {\n                    0 -> mPullRefreshLayout!!.setRefreshOffsetCalculator(QMUIDefaultRefreshOffsetCalculator())\n                    1 -> mPullRefreshLayout!!.setRefreshOffsetCalculator(QMUIFollowRefreshOffsetCalculator())\n                    2 -> mPullRefreshLayout!!.setRefreshOffsetCalculator(QMUICenterGravityRefreshOffsetCalculator())\n                    else -> {}\n                }\n            }\n            .build()\n            .show()\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRadiusImageView2ScaleTypeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView2;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(name = \"QMUIRadiusImageView2 ScaleType\")\npublic class QDRadiusImageView2ScaleTypeFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.image_1)\n    QMUIRadiusImageView2 mRadius1ImageView;\n    @BindView(R.id.image_2)\n    QMUIRadiusImageView2 mRadius2ImageView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_radius_imageview2_scale_type, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n\n        // 动态修改效果按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(\"CENTER\")\n                .addItem(\"CENTER_INSIDE\")\n                .addItem(\"CENTER_CROP\")\n                .addItem(\"FIT_CENTER\")\n                .addItem(\"FIT_XY\")\n                .addItem(\"FIT_START\")\n                .addItem(\"FIT_END\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        switch (position) {\n                            case 0:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER);\n                                break;\n                            case 1:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                                break;\n                            case 2:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);\n                                break;\n                            case 3:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n                                break;\n                            case 4:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_XY);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_XY);\n                                break;\n                            case 5:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_START);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_START);\n                                break;\n                            case 6:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_END);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_END);\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRadiusImageView2UsageFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView2;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIRadiusImageView2} 的使用示例。\n * Created by cgspine on 15/9/15.\n */\n@Widget(name = \"QMUIRadiusImageView2 usage\")\npublic class QDRadiusImageView2UsageFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.radiusImageView)\n    QMUIRadiusImageView2 mRadiusImageView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_radius_imageview2, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n\n        reset();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n\n        // 动态修改效果按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void reset() {\n        mRadiusImageView.setBorderColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_border_color));\n        mRadiusImageView.setBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 2));\n        mRadiusImageView.setCornerRadius(QMUIDisplayHelper.dp2px(getContext(), 10));\n        mRadiusImageView.setSelectedMaskColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_selected_mask_color));\n        mRadiusImageView.setSelectedBorderColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_selected_border_color));\n        mRadiusImageView.setSelectedBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 3));\n        mRadiusImageView.setTouchSelectModeEnabled(true);\n        mRadiusImageView.setCircle(false);\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(getResources().getString(R.string.circularImageView_modify_1))\n                .addItem(getResources().getString(R.string.circularImageView_modify_2))\n                .addItem(getResources().getString(R.string.circularImageView_modify_3))\n                .addItem(getResources().getString(R.string.circularImageView_modify_4))\n                .addItem(getResources().getString(R.string.circularImageView_modify_5))\n                .addItem(getResources().getString(R.string.circularImageView_modify_6))\n                .addItem(getResources().getString(R.string.circularImageView_modify_8))\n                .addItem(getResources().getString(R.string.circularImageView_modify_9))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        reset();\n                        switch (position) {\n                            case 0:\n                                mRadiusImageView.setBorderColor(Color.BLACK);\n                                mRadiusImageView.setBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 4));\n                                break;\n                            case 1:\n                                mRadiusImageView.setSelectedBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 6));\n                                mRadiusImageView.setSelectedBorderColor(Color.GREEN);\n                                break;\n                            case 2:\n                                mRadiusImageView.setSelectedMaskColor(\n                                        ContextCompat.getColor(\n                                                getContext(), R.color.radiusImageView_selected_mask_color));\n                                break;\n                            case 3:\n                                if (mRadiusImageView.isSelected()) {\n                                    mRadiusImageView.setSelected(false);\n                                } else {\n                                    mRadiusImageView.setSelected(true);\n                                }\n                                break;\n                            case 4:\n                                mRadiusImageView.setCornerRadius(QMUIDisplayHelper.dp2px(getContext(), 20));\n                                break;\n                            case 5:\n                                mRadiusImageView.setCircle(true);\n                                break;\n                            case 6:\n                                mRadiusImageView.setTouchSelectModeEnabled(false);\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRadiusImageViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIRadiusImageView.class, iconRes = R.mipmap.icon_grid_radius_image_view)\npublic class QDRadiusImageViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRadiusImageViewUsageFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRadiusImageViewUsageFragment fragment = new QDRadiusImageViewUsageFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRadiusImageView2UsageFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRadiusImageView2UsageFragment fragment = new QDRadiusImageView2UsageFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRadiusImageViewScaleTypeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRadiusImageViewScaleTypeFragment fragment = new QDRadiusImageViewScaleTypeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRadiusImageView2ScaleTypeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRadiusImageView2ScaleTypeFragment fragment = new QDRadiusImageView2ScaleTypeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRadiusImageViewScaleTypeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(name = \"QMUIRadiusImageView ScaleType\")\npublic class QDRadiusImageViewScaleTypeFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.image_1)\n    QMUIRadiusImageView mRadius1ImageView;\n    @BindView(R.id.image_2)\n    QMUIRadiusImageView mRadius2ImageView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_radius_imageview_scale_type, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n\n        // 动态修改效果按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(\"CENTER\")\n                .addItem(\"CENTER_INSIDE\")\n                .addItem(\"CENTER_CROP\")\n                .addItem(\"FIT_CENTER\")\n                .addItem(\"FIT_XY\")\n                .addItem(\"FIT_START\")\n                .addItem(\"FIT_END\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        switch (position) {\n                            case 0:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER);\n                                break;\n                            case 1:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                                break;\n                            case 2:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);\n                                break;\n                            case 3:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n                                break;\n                            case 4:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_XY);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_XY);\n                                break;\n                            case 5:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_START);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_START);\n                                break;\n                            case 6:\n                                mRadius1ImageView.setScaleType(ImageView.ScaleType.FIT_END);\n                                mRadius2ImageView.setScaleType(ImageView.ScaleType.FIT_END);\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRadiusImageViewUsageFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUIRadiusImageView;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIRadiusImageView} 的使用示例。\n * Created by cgspine on 15/9/15.\n */\n@Widget(name = \"QMUIRadiusImageView usage\")\npublic class QDRadiusImageViewUsageFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.radiusImageView)\n    QMUIRadiusImageView mRadiusImageView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_radius_imageview, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n\n        reset();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n\n        // 动态修改效果按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void reset() {\n        mRadiusImageView.setBorderColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_border_color));\n        mRadiusImageView.setBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 2));\n        mRadiusImageView.setCornerRadius(QMUIDisplayHelper.dp2px(getContext(), 10));\n        mRadiusImageView.setSelectedMaskColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_selected_mask_color));\n        mRadiusImageView.setSelectedBorderColor(\n                ContextCompat.getColor(getContext(), R.color.radiusImageView_selected_border_color));\n        mRadiusImageView.setSelectedBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 3));\n        mRadiusImageView.setTouchSelectModeEnabled(true);\n        mRadiusImageView.setCircle(false);\n        mRadiusImageView.setOval(false);\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(getResources().getString(R.string.circularImageView_modify_1))\n                .addItem(getResources().getString(R.string.circularImageView_modify_2))\n                .addItem(getResources().getString(R.string.circularImageView_modify_3))\n                .addItem(getResources().getString(R.string.circularImageView_modify_4))\n                .addItem(getResources().getString(R.string.circularImageView_modify_5))\n                .addItem(getResources().getString(R.string.circularImageView_modify_6))\n                .addItem(getResources().getString(R.string.circularImageView_modify_7))\n                .addItem(getResources().getString(R.string.circularImageView_modify_8))\n                .addItem(getResources().getString(R.string.circularImageView_modify_9))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        reset();\n                        switch (position) {\n                            case 0:\n                                mRadiusImageView.setBorderColor(Color.BLACK);\n                                mRadiusImageView.setBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 4));\n                                break;\n                            case 1:\n                                mRadiusImageView.setSelectedBorderWidth(QMUIDisplayHelper.dp2px(getContext(), 6));\n                                mRadiusImageView.setSelectedBorderColor(Color.GREEN);\n                                break;\n                            case 2:\n                                mRadiusImageView.setSelectedMaskColor(\n                                        ContextCompat.getColor(\n                                                getContext(), R.color.radiusImageView_selected_mask_color));\n                                break;\n                            case 3:\n                                if (mRadiusImageView.isSelected()) {\n                                    mRadiusImageView.setSelected(false);\n                                } else {\n                                    mRadiusImageView.setSelected(true);\n                                }\n                                break;\n                            case 4:\n                                mRadiusImageView.setCornerRadius(QMUIDisplayHelper.dp2px(getContext(), 20));\n                                break;\n                            case 5:\n                                mRadiusImageView.setCircle(true);\n                                break;\n                            case 6:\n                                mRadiusImageView.setOval(true);\n                            case 7:\n                                mRadiusImageView.setTouchSelectModeEnabled(false);\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDRecyclerViewDraggableScrollBarFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIDraggableScrollBar;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVDraggableScrollBar;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIRVDraggableScrollBar.class, iconRes = R.mipmap.icon_grid_scroll_animator)\npublic class QDRecyclerViewDraggableScrollBarFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVDraggableScrollBar scrollBar = new QMUIRVDraggableScrollBar(0, 0, 0);\n        scrollBar.setEnableScrollBarFadeInOut(true);\n        scrollBar.attachToRecyclerView(mRecyclerView);\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_RIGHT;\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDSliderFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.qmuiteam.qmui.widget.QMUISeekBar;\nimport com.qmuiteam.qmui.widget.QMUISlider;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(widgetClass = QMUISlider.class, iconRes = R.mipmap.icon_grid_slider)\n@FragmentScheme(\n        name = \"slider\",\n        activities = {QDMainActivity.class},\n        customMatcher = SliderSchemeMatcher.class\n)\npublic class QDSliderFragment extends BaseFragment implements QMUISlider.Callback {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n\n    @BindView(R.id.slider)\n    QMUISlider mSlider;\n\n    @BindView(R.id.seekBar)\n    QMUISeekBar mSeekBar;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_slider, null);\n        ButterKnife.bind(this, view);\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n\n        initTopBar();\n//        QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();\n//        builder.background(R.attr.qmui_config_color_black);\n//        builder.progressColor(R.attr.qmui_config_color_gray_9);\n//        QMUISkinHelper.setSkinValue(mSlider, builder);\n//        builder.clear();\n//        builder.background(R.attr.qmui_config_color_blue);\n//        builder.border(R.attr.app_skin_btn_test_border);\n//        mSlider.setThumbSkin(builder);\n//        builder.clear();\n        mSlider.setCallback(this);\n        mSeekBar.setCallback(this);\n\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    @Override\n    public void onProgressChange(QMUISlider slider, int progress, int tickCount, boolean fromUser) {\n        Log.i(\"QDSliderFragment\", \"progress = \" + progress + \"; fromUser = \" + fromUser);\n    }\n\n    @Override\n    public void onStartMoving(QMUISlider slider, int progress, int tickCount) {\n\n    }\n\n    @Override\n    public void onStopMoving(QMUISlider slider, int progress, int tickCount) {\n\n    }\n\n    @Override\n    public void onTouchDown(QMUISlider slider, int progress, int tickCount, boolean hitThumb) {\n\n    }\n\n    @Override\n    public void onTouchUp(QMUISlider slider, int progress, int tickCount) {\n\n    }\n\n    @Override\n    public void onLongTouch(QMUISlider slider, int progress, int tickCount) {\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDSpanTouchFixTextViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.method.LinkMovementMethod;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\n\n@Widget(widgetClass = QMUISpanTouchFixTextView.class, iconRes = R.mipmap.icon_grid_span_touch_fix_text_view)\npublic class QDSpanTouchFixTextViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.sysytem_tv_1) TextView mSystemTv1;\n    @BindView(R.id.sysytem_tv_2) TextView mSystemTv2;\n    @BindView(R.id.touch_fix_tv_1) QMUISpanTouchFixTextView mSpanTouchFixTextView1;\n    @BindView(R.id.touch_fix_tv_2) QMUISpanTouchFixTextView mSpanTouchFixTextView2;\n\n    private int highlightTextNormalColor;\n    private int highlightTextPressedColor;\n    private int highlightBgNormalColor;\n    private int highlightBgPressedColor;\n\n    @OnClick({R.id.touch_fix_tv_1, R.id.sysytem_tv_1})\n    void onClickTv(View v) {\n        Toast.makeText(getContext(), \"onClickTv\", Toast.LENGTH_SHORT).show();\n    }\n\n    @OnClick({R.id.click_area_1, R.id.click_area_2})\n    void onClickArea() {\n        Toast.makeText(getContext(), \"onClickArea\", Toast.LENGTH_SHORT).show();\n    }\n\n\n    @Override\n    protected View onCreateView() {\n        highlightTextNormalColor = ContextCompat.getColor(getContext(), R.color.app_color_blue_2);\n        highlightTextPressedColor = ContextCompat.getColor(getContext(), R.color.app_color_blue_2_pressed);\n        highlightBgNormalColor = QMUIResHelper.getAttrColor(getContext(), R.attr.qmui_config_color_gray_8);\n        highlightBgPressedColor = QMUIResHelper.getAttrColor(getContext(), R.attr.qmui_config_color_gray_6);\n\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_touch_span_fix_layout, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n\n        // 场景一\n        mSystemTv1.setMovementMethod(LinkMovementMethod.getInstance());\n        mSystemTv1.setText(generateSp(mSystemTv1, getResources().getString(R.string.system_behavior_1)));\n\n        mSpanTouchFixTextView1.setMovementMethodDefault();\n        mSpanTouchFixTextView1.setText(generateSp(mSystemTv1, getResources().getString(R.string.span_touch_fix_1)));\n\n        // 场景二\n        mSystemTv2.setMovementMethod(LinkMovementMethod.getInstance());\n        mSystemTv2.setText(generateSp(mSystemTv1, getResources().getString(R.string.system_behavior_2)));\n\n        mSpanTouchFixTextView2.setMovementMethodDefault();\n        mSpanTouchFixTextView2.setNeedForceEventToParent(true);\n        mSpanTouchFixTextView2.setText(generateSp(mSpanTouchFixTextView2, getResources().getString(R.string.span_touch_fix_2)));\n\n        return view;\n    }\n\n    private SpannableString generateSp(TextView tv, String text) {\n        String highlight1 = \"@qmui\";\n        String highlight2 = \"#qmui#\";\n        SpannableString sp = new SpannableString(text);\n        int start = 0, end;\n        int index;\n        while ((index = text.indexOf(highlight1, start)) > -1) {\n            end = index + highlight1.length();\n            sp.setSpan(new QMUITouchableSpan(tv,\n                    R.attr.app_skin_span_normal_text_color,\n                    R.attr.app_skin_span_pressed_text_color,\n                    R.attr.app_skin_span_normal_bg_color,\n                    R.attr.app_skin_span_pressed_bg_color) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Toast.makeText(getContext(), \"click @qmui\", Toast.LENGTH_SHORT).show();\n                }\n            }, index, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n            start = end;\n        }\n\n        start = 0;\n        while ((index = text.indexOf(highlight2, start)) > -1) {\n            end = index + highlight2.length();\n            sp.setSpan(new QMUITouchableSpan(tv,\n                    R.attr.app_skin_span_normal_text_color,\n                    R.attr.app_skin_span_pressed_text_color,\n                    R.attr.app_skin_span_normal_bg_color,\n                    R.attr.app_skin_span_pressed_bg_color) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Toast.makeText(getContext(), \"click #qmui#\", Toast.LENGTH_SHORT).show();\n                }\n            }, index, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n            start = end;\n        }\n        return sp;\n    }\n\n    private void initTopBar() {\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegment2FixModeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.content.Context;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.core.content.ContextCompat;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmui.widget.tab.QMUITab;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabIndicator;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment2;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(group = Group.Other, name = \"ViewPager2: 固定宽度，内容均分\")\npublic class QDTabSegment2FixModeFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment)\n    QMUITabSegment2 mTabSegment;\n    @BindView(R.id.contentViewPager)\n    ViewPager2 mContentViewPager;\n\n    private ContentPage mDestPage = ContentPage.Item1;\n    private QDItemDescription mQDItemDescription;\n    private QDRecyclerViewAdapter mPagerAdapter;\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager2_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(getResources().getString(R.string.tabSegment_mode_general))\n                .addItem(getResources().getString(R.string.tabSegment_mode_bottom_indicator))\n                .addItem(getResources().getString(R.string.tabSegment_mode_top_indicator))\n                .addItem(getResources().getString(R.string.tabSegment_mode_indicator_with_content))\n                .addItem(getResources().getString(R.string.tabSegment_mode_left_icon_and_auto_tint))\n                .addItem(getResources().getString(R.string.tabSegment_mode_sign_count))\n                .addItem(getResources().getString(R.string.tabSegment_mode_icon_change))\n                .addItem(getResources().getString(R.string.tabSegment_mode_muti_color))\n                .addItem(getResources().getString(R.string.tabSegment_mode_change_content_by_index))\n                .addItem(getResources().getString(R.string.tabSegment_mode_replace_tab_by_index))\n                .addItem(getResources().getString(R.string.tabSegment_mode_scale_selected))\n                .addItem(getResources().getString(R.string.tabSegment_mode_change_gravity))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        Context context = getContext();\n                        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder()\n                                .setGravity(Gravity.CENTER);\n                        int indicatorHeight = QMUIDisplayHelper.dp2px(context, 2);\n                        switch (position) {\n                            case 0:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 1:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 2:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, true, true));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 3:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, false));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 4: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                tabBuilder.setDynamicChangeIconColor(true);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 5:\n//                                mTabSegment.showSignCountView(getContext(), 0, 20); // 也可以直接调用这个\n                                QMUITab tab = mTabSegment.getTab(0);\n                                tab.setSignCount(20);\n\n                                QMUITab tab1 = mTabSegment.getTab(1);\n                                tab1.setRedPoint();\n                                break;\n                            case 6: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                tabBuilder.setDynamicChangeIconColor(false);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 7: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_blue)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 8:\n                                mTabSegment.updateTabText(0, \"动态更新文案\");\n                                break;\n                            case 9: {\n                                QMUITab newTab = tabBuilder.setText(\"动态更新\")\n                                        .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component))\n                                        .setDynamicChangeIconColor(true)\n                                        .build(getContext());\n                                mTabSegment.replaceTab(0, newTab);\n                                break;\n                            }\n                            case 10: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true)\n                                        .setTextSize(\n                                                QMUIDisplayHelper.sp2px(context, 13),\n                                                QMUIDisplayHelper.sp2px(context, 15))\n                                        .setSelectedIconScale(1.5f);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_blue, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 11: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true)\n                                        .setTextSize(\n                                                QMUIDisplayHelper.sp2px(context, 13),\n                                                QMUIDisplayHelper.sp2px(context, 15))\n                                        .setSelectedIconScale(1.5f)\n                                        .setGravity(Gravity.LEFT | Gravity.BOTTOM);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_blue, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        mTabSegment.notifyDataChanged();\n                    }\n                })\n                .build()\n                .show();\n    }\n\n    private void initTabAndPager() {\n        mPagerAdapter = new QDRecyclerViewAdapter();\n        mPagerAdapter.setItemCount(ContentPage.SIZE);\n        mContentViewPager.setAdapter(mPagerAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        QMUITabBuilder builder = mTabSegment.tabBuilder();\n        mTabSegment.addTab(builder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n        mTabSegment.addTab(builder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n        mTabSegment.notifyDataChanged();\n        mTabSegment.setMode(QMUITabSegment.MODE_FIXED);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n                mTabSegment.clearSignCountView(index);\n            }\n        });\n        mTabSegment.setupWithViewPager(mContentViewPager);\n    }\n\n    public enum ContentPage {\n        Item1(0),\n        Item2(1);\n        public static final int SIZE = 2;\n        private final int position;\n\n        ContentPage(int pos) {\n            position = pos;\n        }\n\n        public static ContentPage getPage(int position) {\n            switch (position) {\n                case 0:\n                    return Item1;\n                case 1:\n                    return Item2;\n                default:\n                    return Item1;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegment2ScrollableModeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabIndicator;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment2;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(group = Group.Other, name = \"ViewPager2: 内容自适应，超过父容器则滚动\")\npublic class QDTabSegment2ScrollableModeFragment extends BaseFragment {\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final int TAB_COUNT = 10;\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment)\n    QMUITabSegment2 mTabSegment;\n    @BindView(R.id.contentViewPager)\n    ViewPager2 mContentViewPager;\n\n    private ContentPage mDestPage = ContentPage.Item1;\n    private QDItemDescription mQDItemDescription;\n    private int mCurrentItemCount = TAB_COUNT;\n    private QDRecyclerViewAdapter mPageAdapter;\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager2_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n        mTopBar.addRightTextButton(\"reduce tab\", QMUIViewHelper.generateViewId())\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        reduceTabCount();\n                    }\n                });\n    }\n\n    private void initTabAndPager() {\n        mPageAdapter = new QDRecyclerViewAdapter();\n        mPageAdapter.setItemCount(mCurrentItemCount);\n        mContentViewPager.setAdapter(mPageAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder();\n        for (int i = 0; i < mCurrentItemCount; i++) {\n            mTabSegment.addTab(tabBuilder.setText(\"Item \" + (i + 1)).build(getContext()));\n        }\n        int space = QMUIDisplayHelper.dp2px(getContext(), 16);\n        mTabSegment.setIndicator(new QMUITabIndicator(\n                QMUIDisplayHelper.dp2px(getContext(), 2), false, true));\n        mTabSegment.setMode(QMUITabSegment.MODE_SCROLLABLE);\n        mTabSegment.setItemSpaceInScrollMode(space);\n        mTabSegment.setupWithViewPager(mContentViewPager);\n        mTabSegment.setPadding(space, 0, space, 0);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n                Toast.makeText(getContext(), \"select index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n                Toast.makeText(getContext(), \"unSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n                Toast.makeText(getContext(), \"reSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n                Toast.makeText(getContext(), \"double tap index \" + index, Toast.LENGTH_SHORT).show();\n            }\n        });\n    }\n\n    private void reduceTabCount() {\n        if (mCurrentItemCount <= 1) {\n            Toast.makeText(getContext(), \"Only the last one, don't reduce it anymore!!!\",\n                    Toast.LENGTH_SHORT).show();\n            return;\n        }\n        mCurrentItemCount--;\n        mPageAdapter.setItemCount(mCurrentItemCount);\n        mPageAdapter.notifyDataSetChanged();\n        mTabSegment.reset();\n        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder();\n        for (int i = 0; i < mCurrentItemCount; i++) {\n            mTabSegment.addTab(tabBuilder.setText(\"Item \" + (i + 1)).build(getContext()));\n        }\n        mTabSegment.notifyDataChanged();\n    }\n\n    public enum ContentPage {\n        Item1(0),\n        Item2(1),\n        Item3(2),\n        Item4(3),\n        Item5(4),\n        Item6(5),\n        Item7(6),\n        Item8(7),\n        Item9(8),\n        Item10(9);\n        private final int position;\n\n        ContentPage(int pos) {\n            position = pos;\n        }\n\n        public static ContentPage getPage(int position) {\n            switch (position) {\n                case 0:\n                    return Item1;\n                case 1:\n                    return Item2;\n                case 2:\n                    return Item3;\n                case 3:\n                    return Item4;\n                case 4:\n                    return Item5;\n                case 5:\n                    return Item6;\n                case 6:\n                    return Item7;\n                case 7:\n                    return Item8;\n                case 8:\n                    return Item9;\n                case 9:\n                    return Item10;\n                default:\n                    return Item1;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegmentFixModeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.content.Context;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmui.widget.tab.QMUITab;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabIndicator;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.core.content.ContextCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-04-28\n */\n\n@LatestVisitRecord\n@Widget(group = Group.Other, name = \"固定宽度，内容均分\")\n@FragmentScheme(\n        name = \"tab\",\n        activities = {QDMainActivity.class},\n        required = {\"mode=1\"},\n        keysWithIntValue = {\"mode\"})\npublic class QDTabSegmentFixModeFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment)\n    QMUITabSegment mTabSegment;\n    @BindView(R.id.contentViewPager)\n    ViewPager mContentViewPager;\n\n    private Map<ContentPage, View> mPageMap = new HashMap<>();\n    private ContentPage mDestPage = ContentPage.Item1;\n    private QDItemDescription mQDItemDescription;\n    private PagerAdapter mPagerAdapter = new PagerAdapter() {\n        @Override\n        public boolean isViewFromObject(View view, Object object) {\n            return view == object;\n        }\n\n        @Override\n        public int getCount() {\n            return ContentPage.SIZE;\n        }\n\n        @Override\n        public Object instantiateItem(final ViewGroup container, int position) {\n            ContentPage page = ContentPage.getPage(position);\n            View view = getPageView(page);\n            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            container.addView(view, params);\n            return view;\n        }\n\n        @Override\n        public void destroyItem(ViewGroup container, int position, Object object) {\n            container.removeView((View) object);\n        }\n\n    };\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        showBottomSheetList();\n                    }\n                });\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(getResources().getString(R.string.tabSegment_mode_general))\n                .addItem(getResources().getString(R.string.tabSegment_mode_bottom_indicator))\n                .addItem(getResources().getString(R.string.tabSegment_mode_top_indicator))\n                .addItem(getResources().getString(R.string.tabSegment_mode_indicator_with_content))\n                .addItem(getResources().getString(R.string.tabSegment_mode_left_icon_and_auto_tint))\n                .addItem(getResources().getString(R.string.tabSegment_mode_sign_count))\n                .addItem(getResources().getString(R.string.tabSegment_mode_icon_change))\n                .addItem(getResources().getString(R.string.tabSegment_mode_muti_color))\n                .addItem(getResources().getString(R.string.tabSegment_mode_change_content_by_index))\n                .addItem(getResources().getString(R.string.tabSegment_mode_replace_tab_by_index))\n                .addItem(getResources().getString(R.string.tabSegment_mode_scale_selected))\n                .addItem(getResources().getString(R.string.tabSegment_mode_change_gravity))\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        Context context = getContext();\n                        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder()\n                                .setGravity(Gravity.CENTER);\n                        int indicatorHeight = QMUIDisplayHelper.dp2px(context, 2);\n                        switch (position) {\n                            case 0:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 1:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 2:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, true, true));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 3:\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, false));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n                                mTabSegment.addTab(tabBuilder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n                                break;\n                            case 4: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                tabBuilder.setDynamicChangeIconColor(true);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 5:\n//                                mTabSegment.showSignCountView(getContext(), 0, 20); // 也可以直接调用这个\n                                QMUITab tab = mTabSegment.getTab(0);\n                                tab.setSignCount(20);\n\n                                QMUITab tab1 = mTabSegment.getTab(1);\n                                tab1.setRedPoint();\n                                break;\n                            case 6: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(null);\n                                tabBuilder.setDynamicChangeIconColor(false);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 7: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_blue)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 8:\n                                mTabSegment.updateTabText(0, \"动态更新文案\");\n                                break;\n                            case 9: {\n                                QMUITab newTab = tabBuilder.setText(\"动态更新\")\n                                        .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component))\n                                        .setDynamicChangeIconColor(true)\n                                        .build(getContext());\n                                mTabSegment.replaceTab(0, newTab);\n                                break;\n                            }\n                            case 10: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true)\n                                        .setTextSize(\n                                                QMUIDisplayHelper.sp2px(context, 13),\n                                                QMUIDisplayHelper.sp2px(context, 15))\n                                        .setSelectedIconScale(1.5f);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_blue, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            case 11: {\n                                mTabSegment.reset();\n                                mTabSegment.setIndicator(new QMUITabIndicator(\n                                        indicatorHeight, false, true));\n                                tabBuilder.setDynamicChangeIconColor(true)\n                                        .setTextSize(\n                                                QMUIDisplayHelper.sp2px(context, 13),\n                                                QMUIDisplayHelper.sp2px(context, 15))\n                                        .setSelectedIconScale(1.5f)\n                                        .setGravity(Gravity.LEFT | Gravity.BOTTOM);\n                                QMUITab component = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected))\n                                        .setText(\"Components\")\n                                        .setColorAttr(R.attr.qmui_config_color_blue, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                QMUITab util = tabBuilder\n                                        .setNormalDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util))\n                                        .setSelectedDrawable(ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected))\n                                        .setText(\"Helper\")\n                                        .setColorAttr(R.attr.qmui_config_color_gray_1, R.attr.qmui_config_color_red)\n                                        .build(getContext());\n                                mTabSegment.addTab(component);\n                                mTabSegment.addTab(util);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        mTabSegment.notifyDataChanged();\n                    }\n                })\n                .build()\n                .show();\n    }\n\n    private void initTabAndPager() {\n        mContentViewPager.setAdapter(mPagerAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        QMUITabBuilder builder = mTabSegment.tabBuilder();\n        mTabSegment.addTab(builder.setText(getString(R.string.tabSegment_item_1_title)).build(getContext()));\n        mTabSegment.addTab(builder.setText(getString(R.string.tabSegment_item_2_title)).build(getContext()));\n        mTabSegment.setupWithViewPager(mContentViewPager, false);\n        mTabSegment.setMode(QMUITabSegment.MODE_FIXED);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n                mTabSegment.clearSignCountView(index);\n            }\n        });\n    }\n\n    private View getPageView(ContentPage page) {\n        View view = mPageMap.get(page);\n        if (view == null) {\n            TextView textView = new TextView(getContext());\n            textView.setGravity(Gravity.CENTER);\n            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);\n            textView.setTextColor(ContextCompat.getColor(getContext(), R.color.app_color_description));\n\n            if (page == ContentPage.Item1) {\n                textView.setText(R.string.tabSegment_item_1_content);\n            } else if (page == ContentPage.Item2) {\n                textView.setText(R.string.tabSegment_item_2_content);\n            }\n\n            view = textView;\n            mPageMap.put(page, view);\n        }\n        return view;\n    }\n\n    public enum ContentPage {\n        Item1(0),\n        Item2(1);\n        public static final int SIZE = 2;\n        private final int position;\n\n        ContentPage(int pos) {\n            position = pos;\n        }\n\n        public static ContentPage getPage(int position) {\n            switch (position) {\n                case 0:\n                    return Item1;\n                case 1:\n                    return Item2;\n                default:\n                    return Item1;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegmentFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2016-10-21\n */\n\n@FragmentScheme(name = \"tab\", activities = {QDMainActivity.class})\n@Widget(widgetClass = QMUITabSegment.class, iconRes = R.mipmap.icon_grid_tab_segment)\npublic class QDTabSegmentFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDTabSegmentFixModeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDTabSegmentFixModeFragment fragment = new QDTabSegmentFixModeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDTabSegmentScrollableModeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDTabSegmentScrollableModeFragment fragment = new QDTabSegmentScrollableModeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDTabSegmentSpaceWeightFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDTabSegmentSpaceWeightFragment fragment = new QDTabSegmentSpaceWeightFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDTabSegment2FixModeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDTabSegment2FixModeFragment fragment = new QDTabSegment2FixModeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDTabSegment2ScrollableModeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDTabSegment2ScrollableModeFragment fragment = new QDTabSegment2ScrollableModeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegmentScrollableModeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\n\nimport android.os.Bundle;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.qmuiteam.qmui.skin.QMUISkinHelper;\nimport com.qmuiteam.qmui.skin.QMUISkinValueBuilder;\nimport com.qmuiteam.qmui.skin.SkinWriter;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabIndicator;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.manager.QDSchemeManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"内容自适应，超过父容器则滚动\")\n@FragmentScheme(\n        name = \"tab\",\n        useRefreshIfCurrentMatched = true,\n        activities = {QDMainActivity.class},\n        required = {\"mode=2\", \"name\"},\n        keysWithIntValue = {\"mode\"})\npublic class QDTabSegmentScrollableModeFragment extends BaseFragment {\n    @SuppressWarnings(\"FieldCanBeLocal\") private final int TAB_COUNT = 10;\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment) QMUITabSegment mTabSegment;\n    @BindView(R.id.contentViewPager) ViewPager mContentViewPager;\n\n    private Map<ContentPage, View> mPageMap = new HashMap<>();\n    private ContentPage mDestPage = ContentPage.Item1;\n    private QDItemDescription mQDItemDescription;\n    private int mCurrentItemCount = TAB_COUNT;\n    private PagerAdapter mPagerAdapter = new PagerAdapter() {\n        @Override\n        public boolean isViewFromObject(View view, Object object) {\n            return view == object;\n        }\n\n        @Override\n        public int getCount() {\n            return mCurrentItemCount;\n        }\n\n        @Override\n        public Object instantiateItem(final ViewGroup container, int position) {\n            ContentPage page = ContentPage.getPage(position);\n            View view = getPageView(page);\n            view.setTag(page);\n            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            container.addView(view, params);\n            return view;\n        }\n\n        @Override\n        public void destroyItem(ViewGroup container, int position, Object object) {\n            container.removeView((View) object);\n        }\n\n        @Override\n        public int getItemPosition(@NonNull Object object) {\n            View view = (View) object;\n            Object page = view.getTag();\n            if (page instanceof ContentPage) {\n                int pos = ((ContentPage) page).getPosition();\n                if (pos >= mCurrentItemCount) {\n                    return POSITION_NONE;\n                }\n                return POSITION_UNCHANGED;\n            }\n            return POSITION_NONE;\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if(isStartedByScheme()){\n            Toast.makeText(getContext(), \"started by scheme\", Toast.LENGTH_SHORT).show();\n\n            Bundle args = getArguments();\n            if(args != null){\n                int mode = args.getInt(\"mode\");\n                String name = args.getString(\"name\");\n                Toast.makeText(getContext(), \"mode = \" + mode + \"; name = \" + name, Toast.LENGTH_SHORT).show();\n            }\n        }\n    }\n\n\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n        mTopBar.addRightTextButton(\"reduce tab\", QMUIViewHelper.generateViewId())\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        reduceTabCount();\n                    }\n                });\n    }\n\n    private void initTabAndPager() {\n        mContentViewPager.setAdapter(mPagerAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder();\n        for (int i = 0; i < mCurrentItemCount; i++) {\n            mTabSegment.addTab(tabBuilder.setText(\"Item \" + (i + 1)).build(getContext()));\n        }\n        int space = QMUIDisplayHelper.dp2px(getContext(), 16);\n        mTabSegment.setIndicator(new QMUITabIndicator(\n                QMUIDisplayHelper.dp2px(getContext(), 2), false, true));\n        mTabSegment.setMode(QMUITabSegment.MODE_SCROLLABLE);\n        mTabSegment.setItemSpaceInScrollMode(space);\n        mTabSegment.setupWithViewPager(mContentViewPager, false);\n        mTabSegment.setPadding(space, 0, space, 0);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n                Toast.makeText(getContext(), \"select index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n                Toast.makeText(getContext(), \"unSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n                Toast.makeText(getContext(), \"reSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n                Toast.makeText(getContext(), \"double tap index \" + index, Toast.LENGTH_SHORT).show();\n            }\n        });\n    }\n\n    private void reduceTabCount() {\n        if (mCurrentItemCount <= 1) {\n            Toast.makeText(getContext(), \"Only the last one, don't reduce it anymore!!!\",\n                    Toast.LENGTH_SHORT).show();\n            return;\n        }\n        mCurrentItemCount--;\n        mPagerAdapter.notifyDataSetChanged();\n        mTabSegment.reset();\n        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder();\n        for (int i = 0; i < mCurrentItemCount; i++) {\n            mTabSegment.addTab(tabBuilder.setText(\"Item \" + (i + 1)).build(getContext()));\n        }\n        mTabSegment.notifyDataChanged();\n    }\n\n    private View getPageView(ContentPage page) {\n        View view = mPageMap.get(page);\n        if (view == null) {\n            TextView textView = new TextView(getContext());\n            textView.setGravity(Gravity.CENTER);\n            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);\n            textView.setTextColor(ContextCompat.getColor(getContext(), R.color.app_color_description));\n            textView.setText(\"这是第 \" + (page.getPosition() + 1) + \" 个 Item 的内容区\");\n            QMUISkinHelper.setSkinValue(textView, new SkinWriter(){\n                @Override\n                public void write(QMUISkinValueBuilder builder) {\n                    builder.textColor(R.attr.app_skin_common_desc_text_color);\n                }\n            });\n            textView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    QDSchemeManager.getInstance().handle(\"qmui://tab?mode=2&name=xixi\");\n                }\n            });\n            view = textView;\n            mPageMap.put(page, view);\n        }\n        return view;\n    }\n\n    public enum ContentPage {\n        Item1(0),\n        Item2(1),\n        Item3(2),\n        Item4(3),\n        Item5(4),\n        Item6(5),\n        Item7(6),\n        Item8(7),\n        Item9(8),\n        Item10(9);\n        private final int position;\n\n        ContentPage(int pos) {\n            position = pos;\n        }\n\n        public static ContentPage getPage(int position) {\n            switch (position) {\n                case 0:\n                    return Item1;\n                case 1:\n                    return Item2;\n                case 2:\n                    return Item3;\n                case 3:\n                    return Item4;\n                case 4:\n                    return Item5;\n                case 5:\n                    return Item6;\n                case 6:\n                    return Item7;\n                case 7:\n                    return Item8;\n                case 8:\n                    return Item9;\n                case 9:\n                    return Item10;\n                default:\n                    return Item1;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n\n    @Override\n    public void refreshFromScheme(@Nullable Bundle bundle) {\n        Toast.makeText(getContext(),\n                \"refreshFromScheme: name = \" + bundle.getString(\"name\"),\n                Toast.LENGTH_SHORT).show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTabSegmentSpaceWeightFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.arch.annotation.FragmentScheme;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.tab.QMUITab;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabIndicator;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-04-28\n */\n\n@Widget(group = Group.Other, name = \"内容自适应，添加weight控制间距\")\n@FragmentScheme(\n        name = \"tab\",\n        activities = {QDMainActivity.class},\n        required = {\"mode=3\"},\n        keysWithIntValue = {\"mode\"})\npublic class QDTabSegmentSpaceWeightFragment extends BaseFragment {\n    @SuppressWarnings(\"FieldCanBeLocal\") private final int TAB_COUNT = 3;\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment) QMUITabSegment mTabSegment;\n    @BindView(R.id.contentViewPager) ViewPager mContentViewPager;\n\n    private Map<ContentPage, View> mPageMap = new HashMap<>();\n    private ContentPage mDestPage = ContentPage.Item1;\n    private QDItemDescription mQDItemDescription;\n    private int mCurrentItemCount = TAB_COUNT;\n    private PagerAdapter mPagerAdapter = new PagerAdapter() {\n        @Override\n        public boolean isViewFromObject(View view, Object object) {\n            return view == object;\n        }\n\n        @Override\n        public int getCount() {\n            return mCurrentItemCount;\n        }\n\n        @Override\n        public Object instantiateItem(final ViewGroup container, int position) {\n            ContentPage page = ContentPage.getPage(position);\n            View view = getPageView(page);\n            view.setTag(page);\n            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            container.addView(view, params);\n            return view;\n        }\n\n        @Override\n        public void destroyItem(ViewGroup container, int position, Object object) {\n            container.removeView((View) object);\n        }\n\n        @Override\n        public int getItemPosition(@NonNull Object object) {\n            View view = (View) object;\n            Object page = view.getTag();\n            if (page instanceof ContentPage) {\n                int pos = ((ContentPage) page).getPosition();\n                if (pos >= mCurrentItemCount) {\n                    return POSITION_NONE;\n                }\n                return POSITION_UNCHANGED;\n            }\n            return POSITION_NONE;\n        }\n    };\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initTabAndPager() {\n        mContentViewPager.setAdapter(mPagerAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        QMUITabBuilder tabBuilder = mTabSegment.tabBuilder();\n        for (int i = 0; i < mCurrentItemCount; i++) {\n            QMUITab tab = tabBuilder.setText(\"Item \" + i).build(getContext());\n            if (i == 0) {\n                tab.setSpaceWeight(0f, 1f);\n            }\n            mTabSegment.addTab(tab);\n        }\n        int space = QMUIDisplayHelper.dp2px(getContext(), 16);\n        mTabSegment.setIndicator(\n                new QMUITabIndicator(QMUIDisplayHelper.dp2px(getContext(), 2),\n                        false, true));\n        mTabSegment.setMode(QMUITabSegment.MODE_SCROLLABLE);\n        mTabSegment.setItemSpaceInScrollMode(space);\n        mTabSegment.setupWithViewPager(mContentViewPager, false);\n        mTabSegment.setPadding(space, 0, space, 0);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n                Toast.makeText(getContext(), \"select index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n                Toast.makeText(getContext(), \"unSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n                Toast.makeText(getContext(), \"reSelect index \" + index, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n                Toast.makeText(getContext(), \"double tap index \" + index, Toast.LENGTH_SHORT).show();\n            }\n        });\n    }\n\n    private View getPageView(ContentPage page) {\n        View view = mPageMap.get(page);\n        if (view == null) {\n            TextView textView = new TextView(getContext());\n            textView.setGravity(Gravity.CENTER);\n            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);\n            textView.setTextColor(ContextCompat.getColor(getContext(), R.color.app_color_description));\n            textView.setText(\"这是第 \" + (page.getPosition() + 1) + \" 个 Item 的内容区\");\n            textView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    startFragment(new QDTabSegmentSpaceWeightFragment());\n                }\n            });\n            view = textView;\n            mPageMap.put(page, view);\n        }\n        return view;\n    }\n\n    public enum ContentPage {\n        Item1(0),\n        Item2(1),\n        Item3(2),\n        ;\n        private final int position;\n\n        ContentPage(int pos) {\n            position = pos;\n        }\n\n        public static ContentPage getPage(int position) {\n            switch (position) {\n                case 0:\n                    return Item1;\n                case 1:\n                    return Item2;\n                case 2:\n                    return Item3;\n                default:\n                    return Item1;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDTipDialogFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUITipDialog;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUITipDialog} 的使用示例。\n * Created by Kayo on 2016/11/21.\n */\n@Widget(widgetClass = QMUITipDialog.class, iconRes = R.mipmap.icon_grid_tip_dialog)\npublic class QDTipDialogFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.listview)\n    ListView mListView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_listview, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initListView() {\n        String[] listItems = new String[]{\n                \"Loading 类型提示框\",\n                \"成功提示类型提示框\",\n                \"失败提示类型提示框\",\n                \"信息提示类型提示框\",\n                \"单独图片类型提示框\",\n                \"单独文字类型提示框\",\n                \"自定义内容提示框\"\n        };\n        List<String> data = new ArrayList<>();\n\n        Collections.addAll(data, listItems);\n\n        mListView.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.simple_list_item, data));\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                final QMUITipDialog tipDialog;\n                switch (position) {\n                    case 1:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setIconType(QMUITipDialog.Builder.ICON_TYPE_SUCCESS)\n                                .setTipWord(\"发送成功\")\n                                .create();\n                        break;\n                    case 2:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setIconType(QMUITipDialog.Builder.ICON_TYPE_FAIL)\n                                .setTipWord(\"发送失败\")\n                                .create();\n                        break;\n                    case 3:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setIconType(QMUITipDialog.Builder.ICON_TYPE_INFO)\n                                .setTipWord(\"请勿重复操作\")\n                                .create();\n                        break;\n                    case 4:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setIconType(QMUITipDialog.Builder.ICON_TYPE_SUCCESS)\n                                .create();\n                        break;\n                    case 5:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setTipWord(\"请勿重复操作\")\n                                .create();\n                        break;\n                    case 6:\n                        tipDialog = new QMUITipDialog.CustomBuilder(getContext())\n                                .setContent(R.layout.tipdialog_custom)\n                                .create();\n                        break;\n                    default:\n                        tipDialog = new QMUITipDialog.Builder(getContext())\n                                .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)\n                                .setTipWord(\"正在加载\")\n                                .create();\n                }\n                tipDialog.show();\n                mListView.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        tipDialog.dismiss();\n                    }\n                }, 1500);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/QDVerticalTextViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.EditText;\n\nimport com.qmuiteam.qmui.util.QMUIKeyboardHelper;\nimport com.qmuiteam.qmui.util.QMUILangHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.QMUIVerticalTextView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIVerticalTextView.class, iconRes = R.mipmap.icon_grid_vertical_text_view)\npublic class QDVerticalTextViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.verticalTextView)\n    QMUIVerticalTextView mVerticalTextView;\n    @BindView(R.id.verticalTextView_editText)\n    EditText mEditText;\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_verticaltextview, null);\n        ButterKnife.bind(this, rootView);\n\n        initTopBar();\n        initVerticalTextView();\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        QDItemDescription qdItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        mTopBar.setTitle(qdItemDescription.getName());\n    }\n\n    private void initVerticalTextView() {\n        final String defaultText = String.format(\"%s 实现对文字的垂直排版。并且对非 CJK (中文、日文、韩文)字符做90度旋转排版。可以在下方的输入框中输入文字，体验不同文字垂直排版的效果。\",\n                QMUIVerticalTextView.class.getSimpleName());\n        mVerticalTextView.setText(defaultText);\n        mEditText.addTextChangedListener(new TextWatcher() {\n            @Override\n            public void beforeTextChanged(CharSequence s, int start, int count, int after) {\n\n            }\n\n            @Override\n            public void onTextChanged(CharSequence s, int start, int before, int count) {\n\n            }\n\n            @Override\n            public void afterTextChanged(Editable s) {\n                mVerticalTextView.setText(QMUILangHelper.isNullOrEmpty(s) ? defaultText : s);\n            }\n        });\n    }\n\n    @Override\n    protected void popBackStack() {\n        super.popBackStack();\n        QMUIKeyboardHelper.hideKeyboard(mEditText);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/SliderSchemeMatcher.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components;\n\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.arch.scheme.QMUIDefaultSchemeMatcher;\nimport com.qmuiteam.qmui.arch.scheme.SchemeItem;\n\nimport java.util.Map;\n\npublic class SliderSchemeMatcher extends QMUIDefaultSchemeMatcher {\n\n    @Override\n    public boolean match(SchemeItem schemeItem, @Nullable Map<String, String> params) {\n        if (params != null) {\n            try {\n                String modeStr = params.get(\"mode\");\n                if (modeStr != null && !modeStr.isEmpty()) {\n                    int mode = Integer.parseInt(modeStr);\n                    return mode > 4;\n                }\n            } catch (Throwable ignore) {\n\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/pullLayout/QDPullFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.pullLayout;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIPullLayout.class, iconRes = R.mipmap.icon_grid_pull_layout)\npublic class QDPullFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n\n        injectDocToTopBar(mTopBar);\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDPullVerticalTestFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDPullVerticalTestFragment fragment = new QDPullVerticalTestFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDPullHorizontalTestFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDPullHorizontalTestFragment fragment = new QDPullHorizontalTestFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDPullRefreshAndLoadMoreTestFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDPullRefreshAndLoadMoreTestFragment fragment = new QDPullRefreshAndLoadMoreTestFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/pullLayout/QDPullHorizontalTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.pullLayout;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.PagerSnapHelper;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@LatestVisitRecord\n@Widget(group = Group.Other, name = \"PullLayout: Horizontal Test\")\npublic class QDPullHorizontalTestFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private QDRecyclerViewAdapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_horizontal_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 1000);\n            }\n        });\n        LinearLayoutManager layoutManager =  new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);\n        mRecyclerView.setLayoutManager(layoutManager);\n        new PagerSnapHelper().attachToRecyclerView(mRecyclerView);\n        mAdapter = new QDRecyclerViewAdapter();\n        mAdapter.setItemCount(10);\n        mRecyclerView.setAdapter(mAdapter);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/pullLayout/QDPullRefreshAndLoadMoreTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.pullLayout;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@LatestVisitRecord\n@Widget(group = Group.Other, name = \"PullLayout: Refresh And LoadMore\")\npublic class QDPullRefreshAndLoadMoreTestFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if(pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP){\n                            onRefreshData();\n                        }else if(pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM){\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData(){\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for(int i = 0; i < 10; i++){\n            data.add(\"onRefreshData-\" + id + \"-\"+ i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore(){\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for(int i = 0; i < 10; i++){\n            data.add(\"onLoadMore-\" + id + \"-\"+ i);\n        }\n        mAdapter.append(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/pullLayout/QDPullVerticalTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.pullLayout;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@LatestVisitRecord\n@Widget(group = Group.Other, name = \"PullLayout: Vertical Test\")\npublic class QDPullVerticalTestFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_vertical_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 1000);\n            }\n        });\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2016-12-22\n */\n@Widget(group = Group.Lab, widgetClass = QMUIQQFaceView.class, iconRes = R.mipmap.icon_grid_qq_face_view)\npublic class QDQQFaceFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDQQFaceUsageFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDQQFaceUsageFragment fragment = new QDQQFaceUsageFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDQQFacePerformanceTestFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDQQFacePerformanceTestFragment fragment = new QDQQFacePerformanceTestFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFacePerformanceTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface;\n\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDTabSegmentFixModeFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.qqface.pageView.QDEmojiconPagerView;\nimport com.qmuiteam.qmuidemo.fragment.components.qqface.pageView.QDQQFacePagerView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-06-08\n */\n\n@Widget(group = Group.Other, name = \"性能观测[微笑]\")\npublic class QDQQFacePerformanceTestFragment extends BaseFragment {\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabSegment) QMUITabSegment mTabSegment;\n    @BindView(R.id.contentViewPager) ViewPager mContentViewPager;\n\n    private Map<Page, View> mPageMap = new HashMap<>();\n    private Page mDestPage = Page.QMUIQQFaceView;\n    private PagerAdapter mPagerAdapter = new PagerAdapter() {\n        @Override\n        public boolean isViewFromObject(View view, Object object) {\n            return view == object;\n        }\n\n        @Override\n        public int getCount() {\n            return QDTabSegmentFixModeFragment.ContentPage.SIZE;\n        }\n\n        @Override\n        public Object instantiateItem(final ViewGroup container, int position) {\n            Page page = Page.getPage(position);\n            View view = getPageView(page);\n            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            container.addView(view, params);\n            return view;\n        }\n\n        @Override\n        public void destroyItem(ViewGroup container, int position, Object object) {\n            container.removeView((View) object);\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            Page page = Page.getPage(position);\n            if (page == Page.QMUIQQFaceView) {\n                return \"QMUI实现方案性能\";\n            } else {\n                return \"ImageSpan实现方案性能\";\n            }\n        }\n    };\n\n    @Override\n    protected View onCreateView() {\n        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab_viewpager_layout, null);\n        ButterKnife.bind(this, rootView);\n\n        initTopBar();\n        initTabAndPager();\n\n        return rootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n    }\n\n    private void initTabAndPager() {\n        mContentViewPager.setAdapter(mPagerAdapter);\n        mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);\n        mTabSegment.setupWithViewPager(mContentViewPager, true);\n        mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() {\n            @Override\n            public void onTabSelected(int index) {\n            }\n\n            @Override\n            public void onTabUnselected(int index) {\n\n            }\n\n            @Override\n            public void onTabReselected(int index) {\n            }\n\n            @Override\n            public void onDoubleTap(int index) {\n\n            }\n        });\n    }\n\n    private View getPageView(Page page) {\n        View view = mPageMap.get(page);\n        if (view == null) {\n            if (page == Page.QMUIQQFaceView) {\n                view = new QDQQFacePagerView(getContext());\n            } else if (page == Page.EmojiconTextView) {\n                view = new QDEmojiconPagerView(getContext());\n            }\n            mPageMap.put(page, view);\n        }\n        return view;\n    }\n\n    public enum Page {\n        QMUIQQFaceView(0),\n        EmojiconTextView(1);\n        private final int position;\n\n        Page(int pos) {\n            position = pos;\n        }\n\n        public static Page getPage(int position) {\n            switch (position) {\n                case 0:\n                    return QMUIQQFaceView;\n                case 1:\n                    return EmojiconTextView;\n                default:\n                    return QMUIQQFaceView;\n            }\n        }\n\n        public int getPosition() {\n            return position;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceTestData.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface;\n\nimport android.graphics.Color;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\n\nimport java.util.ArrayList;\n\n/**\n * @author cginechen\n * @date 2016-12-22\n */\n\npublic class QDQQFaceTestData {\n    private ArrayList<CharSequence> mList = new ArrayList<>();\n\n    public QDQQFaceTestData() {\n        for (int i = 0; i < 100; i++) {\n            String topic = \"#表情[发呆][微笑]大战\";\n            String at = \"@伟大的[发呆]工程师\";\n            String url = \"https://qmuiteam.com?abcdefchigklmnopqrst\";\n            String text = \"index = \" + i + \" : \" + at + \"，人生就是要不断[微笑]\\n[微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑]\" + url + \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"[得意][微笑][撇嘴][色][微笑][得意][流泪][害羞][闭嘴][睡][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][惊讶][微笑][微笑][微笑][微笑][发怒][微笑]\\n[微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][调皮][微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][呲牙][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"人生就是要不断[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\\n\\n[微笑][微笑][微笑]\" +\n                    \"[微笑][微笑]也会出现不合格的[这是不合格的标签]其它表情[发呆][发呆][发呆][发呆][发呆] \" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][发呆][发呆][发呆][发呆] \" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" + topic + \"[微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][微笑][微笑][微笑]\\n[微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                    \"[微笑][微笑][微笑][微笑][微笑][发呆][发呆][发呆][发呆][发呆][微笑][微笑][微笑]\";\n            SpannableString sb = new SpannableString(text);\n            sb.setSpan(new QMUITouchableSpan(Color.BLUE, Color.BLACK, Color.GRAY, Color.GREEN) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Toast.makeText(widget.getContext(), \"点击了@\", Toast.LENGTH_SHORT).show();\n                }\n            }, text.indexOf(at), text.indexOf(at) + at.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n\n\n            sb.setSpan(new QMUITouchableSpan(Color.RED, Color.BLACK, Color.YELLOW, Color.GREEN) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Toast.makeText(widget.getContext(), \"点击了url\", Toast.LENGTH_SHORT).show();\n                }\n            }, text.indexOf(url), text.indexOf(url) + url.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n\n\n            sb.setSpan(new QMUITouchableSpan(Color.RED, Color.BLACK, Color.YELLOW, Color.GREEN) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Toast.makeText(widget.getContext(), \"点击了话题\", Toast.LENGTH_SHORT).show();\n                }\n            }, text.indexOf(topic), text.indexOf(topic) + topic.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n\n            mList.add(sb);\n        }\n\n    }\n\n    public ArrayList<CharSequence> getList() {\n        return mList;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/QDQQFaceUsageFragment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.fragment.components.qqface\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.text.SpannableString\nimport android.text.Spanned\nimport android.text.TextUtils\nimport android.text.style.LineHeightSpan\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.TextView\nimport android.widget.Toast\nimport androidx.core.content.ContextCompat\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmui.kotlin.onClick\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView\nimport com.qmuiteam.qmui.span.QMUITouchableSpan\nimport com.qmuiteam.qmui.type.SerialLineIndentHandler\nimport com.qmuiteam.qmui.type.parser.EmojiTextParser\nimport com.qmuiteam.qmui.type.parser.TextParser\nimport com.qmuiteam.qmui.type.view.LineTypeView\nimport com.qmuiteam.qmui.type.view.MarqueeTypeView\nimport com.qmuiteam.qmui.util.QMUIColorHelper\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmuidemo.QDQQFaceManager\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport com.qmuiteam.qmuidemo.lib.Group\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport com.qmuiteam.qmuidemo.manager.QDDataManager\nimport java.util.regex.Pattern\n\n/**\n * @author cginechen\n * @date 2016-12-24\n */\n\nclass B(val mHeight: Int): LineHeightSpan {\n    override fun chooseHeight(text: CharSequence?, start: Int, end: Int, spanstartv: Int, lineHeight: Int, fm: Paint.FontMetricsInt) {\n\n        // 参考官方 API 29 提供的 Standard 而进行修改\n        if (fm.descent <= fm.bottom && fm.ascent >= fm.top) {\n            if (fm.descent > mHeight) {\n                // Show as much descent as possible\n                fm.descent = Math.min(mHeight, fm.descent)\n                fm.bottom = fm.descent\n                fm.ascent = 0\n                fm.top = fm.ascent\n            } else if (-fm.ascent + fm.descent > mHeight) {\n                // Show all descent, and as much ascent as possible\n                fm.bottom = fm.descent\n                fm.ascent = -mHeight + fm.descent\n                fm.top = fm.ascent\n            } else {\n                // Show proportionally additional ascent / top & descent / bottom\n                val additional: Int = mHeight - (-fm.top + fm.bottom)\n\n                // Round up for the negative values and down for the positive values  (arbitrary choice)\n                // So that bottom - top equals additional even if it's an odd number.\n                fm.top -= Math.ceil((additional / 2.0f).toDouble()).toInt()\n                fm.bottom += Math.floor((additional / 2.0f).toDouble()).toInt()\n                fm.ascent = fm.top\n                fm.descent = fm.bottom\n            }\n        } else {\n            val originHeight = fm.descent - fm.ascent\n            // If original height is not positive, do nothing.\n            if (originHeight <= 0) {\n                return\n            }\n            if (originHeight < mHeight) {\n                // Show proportionally additional ascent / top & descent / bottom\n                val additional: Int = mHeight - originHeight\n\n                // Round up for the negative values and down for the positive values  (arbitrary choice)\n                // So that bottom - top equals additional even if it's an odd number.\n                fm.ascent -= Math.ceil((additional / 2.0f).toDouble()).toInt()\n                fm.top = fm.ascent\n                fm.descent += Math.floor((additional / 2.0f).toDouble()).toInt()\n                fm.bottom = fm.descent\n            } else {\n                var ratio: Float = mHeight * 1.0f / originHeight\n                fm.descent = Math.round(fm.descent * ratio)\n                fm.ascent = fm.descent - mHeight\n                ratio = mHeight * 1.0f / (fm.bottom - fm.top)\n                fm.bottom = Math.round(fm.bottom * ratio)\n                fm.top = fm.bottom - mHeight\n            }\n        }\n    }\n\n}\n\nclass Test(context: Context, attrs: AttributeSet): TextView(context, attrs){\n\n    init {\n        setBackgroundColor(Color.RED)\n        text = SpannableString(\"呵呵བོད་སྐད\").apply {\n            setSpan(B(80), 0, length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)\n        }\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(80, MeasureSpec.EXACTLY))\n    }\n}\n\n@Widget(group = Group.Other, name = \"QQ表情使用展示\")\n@LatestVisitRecord\nclass QDQQFaceUsageFragment : BaseFragment() {\n    @JvmField\n    @BindView(R.id.topbar)\n    var mTopBar: QMUITopBarLayout? = null\n\n    @JvmField\n    @BindView(R.id.marquee1)\n    var mMarqueeTypeView1: MarqueeTypeView? = null\n\n    @JvmField\n    @BindView(R.id.marquee2)\n    var mMarqueeTypeView2: MarqueeTypeView? = null\n\n    @JvmField\n    @BindView(R.id.line_type_1)\n    var mLineType1: LineTypeView? = null\n\n    @JvmField\n    @BindView(R.id.line_type_2)\n    var mLineType2: LineTypeView? = null\n\n    @JvmField\n    @BindView(R.id.line_type_3)\n    var mLineType3: LineTypeView? = null\n\n    @JvmField\n    @BindView(R.id.qqface1)\n    var mQQFace1: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface2)\n    var mQQFace2: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface3)\n    var mQQFace3: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface4)\n    var mQQFace4: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface5)\n    var mQQFace5: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface6)\n    var mQQFace6: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface7)\n    var mQQFace7: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface8)\n    var mQQFace8: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface9)\n    var mQQFace9: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface10)\n    var mQQFace10: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface11)\n    var mQQFace11: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface12)\n    var mQQFace12: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface13)\n    var mQQFace13: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface14)\n    var mQQFace14: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface15)\n    var mQQFace15: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface16)\n    var mQQFace16: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface17)\n    var mQQFace17: QMUIQQFaceView? = null\n\n    @JvmField\n    @BindView(R.id.qqface18)\n    var mQQFace18: QMUIQQFaceView? = null\n    override fun onCreateView(): View {\n        val view = LayoutInflater.from(context).inflate(R.layout.fragment_qqface_layout, null)\n        ButterKnife.bind(this, view)\n        initTopBar()\n        initData()\n        return view\n    }\n\n    private fun initTopBar() {\n        mTopBar!!.addLeftBackImageButton().setOnClickListener { popBackStack() }\n        mTopBar!!.setTitle(QDDataManager.getInstance().getName(this.javaClass))\n    }\n\n    private fun initData() {\n        val textParser: TextParser = EmojiTextParser(QDQQFaceManager.getInstance()) { true }\n        mMarqueeTypeView1!!.fadeWidth = QMUIDisplayHelper.dp2px(context, 40).toFloat()\n        mMarqueeTypeView1!!.textParser = textParser\n        mMarqueeTypeView1!!.text = \"🙃🙃🙃🙃飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀飘呀这是一行很长很长[微笑][微笑][微笑][微笑]的文本，但是[微笑][微笑][微笑][微笑]只能单行显示\"\n        mMarqueeTypeView2!!.fadeWidth = QMUIDisplayHelper.dp2px(context, 40).toFloat()\n        mMarqueeTypeView2!!.textParser = textParser\n        mMarqueeTypeView2!!.text = \"[大哭]我太短了，实在是飘不动了\"\n        mLineType1!!.textParser = textParser\n        val lineLayout = mLineType1!!.lineLayout\n        lineLayout.maxLines = 6\n        lineLayout.ellipsize = TextUtils.TruncateAt.END\n        lineLayout.moreText = \"更多\"\n        lineLayout.moreUnderlineHeight = QMUIDisplayHelper.dp2px(context, 2)\n        lineLayout.moreTextColor = Color.RED\n        lineLayout.moreUnderlineColor = Color.BLUE\n        mLineType1!!.lineHeight = QMUIDisplayHelper.dp2px(context, 36)\n        mLineType1!!.textColor = Color.BLACK\n        mLineType1!!.textSize = QMUIDisplayHelper.sp2px(context, 15).toFloat()\n        mLineType1!!.text = \"QMUI Android 的设计[微笑]目的🙃🙃🙃🙃是用于辅助快速搭建一个具备基本设计还原[微笑]效果的 Android 项目，\" +\n                \"同时利用自身[微笑]提供的丰富控件及兼容处理，让开[微笑]发者能专注于业务需求而无需耗费[微笑]精力在基础代[微笑]码的设计上。\" +\n                \"不管是新项目的创建，或是已有项[微笑]目的维护，均可使开[微笑]发效率和项目[微笑]质量得到大幅度提升。\"\n        mLineType1!!.addBgEffect(10, 16, QMUIColorHelper.setColorAlpha(Color.RED, 0.5f))\n\n        mLineType1!!.addClickEffect(20, 30,\n            { isPressed -> if (isPressed) Color.RED else Color.BLUE },\n            { isPressed -> if (isPressed) Color.BLUE else Color.RED }\n        ) { start, end ->\n            Toast.makeText(context, \"你点${start}-${end}干嘛\", Toast.LENGTH_SHORT).show()\n        }\n\n        mLineType1!!.addClickEffect(44, 82,\n            { isPressed -> if (isPressed) Color.RED else Color.BLUE },\n            { isPressed -> if (isPressed) Color.BLUE else Color.RED }\n        ) { start, end ->\n            Toast.makeText(context, \"你点${start}-${end}干嘛\", Toast.LENGTH_SHORT).show()\n        }\n\n        mLineType1!!.onClick {\n            Toast.makeText(context, \"你点整个 LineTypeView 干嘛\", Toast.LENGTH_SHORT).show()\n        }\n\n        mLineType2!!.textParser = textParser\n        mLineType2!!.lineHeight = QMUIDisplayHelper.dp2px(context, 36)\n        mLineType2!!.textColor = Color.BLACK\n        mLineType2!!.textSize = QMUIDisplayHelper.sp2px(context, 15).toFloat()\n        val content2 = \"a.这一条很重要，你要仔细研读研读。\\n\" +\n                \"b.这一条不重要，但是有很多很多很多很多很多很多很多很多内容。。\\n\" +\n                \"c.这一条特别重要，但是我也不知道对不对，只能放这里了，哈哈哈哈。\\n\"\n        mLineType2!!.text = content2\n\n        val pairs = arrayListOf<Pair<Int, Int>>()\n        val pattern = Pattern.compile(\"([a-z]+\\\\.)\")\n        val matcher = pattern.matcher(content2)\n        while (matcher.find()){\n            pairs.add(matcher.start() to matcher.end() - 1)\n        }\n\n        pairs.forEach {\n            mLineType2!!.addTextColorEffect(it.first, it.second, Color.LTGRAY)\n        }\n        mLineType2!!.lineLayout.lineIndentHandler = SerialLineIndentHandler(pairs)\n\n\n        mLineType3!!.textParser = textParser\n        mLineType3!!.lineHeight = QMUIDisplayHelper.dp2px(context, 36)\n        mLineType3!!.textColor = Color.BLACK\n        mLineType3!!.textSize = QMUIDisplayHelper.sp2px(context, 15).toFloat()\n        mLineType3!!.text = \"འདི་བཞིན་གྱི་ཡིད་བརྙན་གྱི་ཚོགས་མང་པོ་ཞིག་གིས་ཞེ་དྲག་བསམ་གཞིག་གི་བར་སྟོང་ཡངས་པོར་ཕྱེས་འགྲོ\"\n\n\n        mQQFace1!!.text = \"这是一行很长很长[微笑][微笑][微笑][微笑]的文本，但是[微笑][微笑][微笑][微笑]只能单行显示\"\n        mQQFace2!!.text = \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行。\"\n        mQQFace3!!.text = \"这是一行很长很长[微笑][微笑][微笑][微笑]的文本，但是[微笑][微笑][微笑][微笑]只能单行显示\"\n        mQQFace4!!.text = \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行。\"\n        mQQFace5!!.text = \"这是一行很长很长[微笑][微笑][微笑][微笑]的文本，但是[微笑][微笑][微笑][微笑]只能单行显示\"\n        mQQFace6!!.text = \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行；\" +\n                \"这是一段很长很长[微笑][微笑][微笑][微笑]的文本，但是最多只能显示三行。\"\n        mQQFace7!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace8!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace9!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace10!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace11!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace12!!.text = \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑]\"\n        mQQFace13!!.text = \"表情可以和字体一起变大[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑][微笑]\" +\n                \"[微笑][微笑][微笑][微笑][微笑]\"\n        val topic = \"#[发呆][微笑]话题\"\n        val text = \"这是一段文本，为了测量 span 的点击在不同 Gravity 下能否正常工作。$topic\"\n        val sb = SpannableString(text)\n        val span: QMUITouchableSpan = object : QMUITouchableSpan(\n            mQQFace14,\n            R.attr.app_skin_span_normal_text_color,\n            R.attr.app_skin_span_pressed_text_color,\n            R.attr.app_skin_span_normal_bg_color,\n            R.attr.app_skin_span_pressed_bg_color\n        ) {\n            override fun onSpanClick(widget: View) {\n                Toast.makeText(widget.context, \"点击了话题\", Toast.LENGTH_SHORT).show()\n            }\n        }\n        span.setIsNeedUnderline(true)\n        sb.setSpan(span, text.indexOf(topic), text.indexOf(topic) + topic.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)\n        mQQFace14!!.text = sb\n        mQQFace15!!.text = sb\n        mQQFace15!!.setLinkUnderLineColor(Color.RED)\n        mQQFace16!!.text = sb\n        mQQFace16!!.setLinkUnderLineHeight(QMUIDisplayHelper.dp2px(context, 4))\n        mQQFace16!!.setLinkUnderLineColor(ContextCompat.getColorStateList(requireContext(), R.color.s_app_color_blue_to_red))\n        mQQFace15!!.gravity = Gravity.CENTER\n        mQQFace16!!.gravity = Gravity.RIGHT\n        mQQFace17!!.setLinkUnderLineColor(Color.RED)\n        mQQFace17!!.setNeedUnderlineForMoreText(true)\n        mQQFace17!!.text = \"这是一段文本，为了测量更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多\" +\n                \"更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多\" +\n                \"更多更多更多更多更多更多更多更多更多更多更多更多更多更多更多的显示情况\"\n        mQQFace18!!.setParagraphSpace(QMUIDisplayHelper.dp2px(context, 20))\n        mQQFace18!!.text = \"\"\"\n            这是一段文本，为[微笑]了测量多段落[微笑]\n            这是一段文本，为[微笑]了测量多段落[微笑]\n            这是一段文本，为[微笑]了测量多段落[微笑]\n            \"\"\".trimIndent()\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/EmojiCache.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport androidx.core.content.ContextCompat;\nimport androidx.collection.LruCache;\n\npublic class EmojiCache {\n\t//caceh里面默认只存放32个表情\n\tprivate static final int EMOJI_CACHE_SIZE = 32;\n\tprivate static EmojiCache _instance;\n\tpublic static void createInstance(int cacheSize) {\n\t\tif(_instance == null) {\n\t\t\t_instance = new EmojiCache(cacheSize);\n\t\t}\n\t}\n\tpublic static EmojiCache getInstance() {\n\t\tif (_instance == null) {\n\t\t\tcreateInstance(EMOJI_CACHE_SIZE);\n\t\t}\n\n\t\treturn _instance;\n\t}\n\t\n\tprivate LruCache<Integer, Drawable> mCache;\n\tpublic EmojiCache(int cacheSize) {\n\t\tmCache = new LruCache<Integer, Drawable>(cacheSize) {\n            @Override\n            protected int sizeOf(Integer key, Drawable value) {\n                return 1;\n            }\n            \n            @Override\n            protected void entryRemoved(boolean evicted, Integer key, Drawable oldValue, Drawable newValue) {\n            \t//这种情况，可能该drawable还在页面使用中，不能随便recycle。这里解除引用即可，gc会自动清除\n//            \tif (oldValue instanceof BitmapDrawable) {\n//\t\t\t\t\t((BitmapDrawable)oldValue).getBitmap().recycle();\n//\t\t\t\t}\n            }\n        };\n\t}\n\t\n\tpublic Drawable getDrawable(Context context, int resourceId) {\n\t\tDrawable drawable = mCache.get(resourceId);\n\t\tif (drawable == null) {\n\t\t\tdrawable = ContextCompat.getDrawable(context, resourceId);\n\t\t\tmCache.put(resourceId, drawable);\n\t\t}\n\t\t\n\t\treturn drawable;\n\t}\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/EmojiconHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon;\n\nimport android.content.Context;\nimport androidx.collection.ArrayMap;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.util.Log;\nimport android.util.SparseIntArray;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.R;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n\n/**\n * 用于作性能比较的控件。\n */\n@SuppressWarnings({\"JavaDoc\", \"UnusedReturnValue\", \"unused\", \"WeakerAccess\"})\npublic final class EmojiconHandler {\n    private static final HashMap<String, Integer> sQQFaceMap = new HashMap<>();\n    private static final List<QQFace> mQQFaceList = new ArrayList<>();\n    private static final SparseIntArray sEmojisMap = new SparseIntArray(846);\n    private static final SparseIntArray sSoftbanksMap = new SparseIntArray(471);\n    private static final ArrayMap<String, String> mQQFaceFileNameList = new ArrayMap<>();//存储QQ表情对应的文件名,方便混淆后可以获取到原文件名\n    /**\n     * 表情的放大倍数\n     */\n    private static final float EMOJIICON_SCALE = 1.2f;\n    /**\n     * 表情的偏移值\n     */\n    private static final int EMOJIICON_TRANSLATE_Y = 0;\n    private static final int QQFACE_TRANSLATE_Y = QMUIDisplayHelper.dpToPx(1);\n\n    static {\n        long start = System.currentTimeMillis();\n\n        mQQFaceList.add(new QQFace(\"[微笑]\", R.drawable.smiley_0));\n        mQQFaceList.add(new QQFace(\"[撇嘴]\", R.drawable.smiley_1));\n        mQQFaceList.add(new QQFace(\"[色]\", R.drawable.smiley_2));\n        mQQFaceList.add(new QQFace(\"[发呆]\", R.drawable.smiley_3));\n        mQQFaceList.add(new QQFace(\"[得意]\", R.drawable.smiley_4));\n        mQQFaceList.add(new QQFace(\"[流泪]\", R.drawable.smiley_5));\n        mQQFaceList.add(new QQFace(\"[害羞]\", R.drawable.smiley_6));\n        mQQFaceList.add(new QQFace(\"[闭嘴]\", R.drawable.smiley_7));\n        mQQFaceList.add(new QQFace(\"[睡]\", R.drawable.smiley_8));\n        mQQFaceList.add(new QQFace(\"[大哭]\", R.drawable.smiley_9));\n        mQQFaceList.add(new QQFace(\"[尴尬]\", R.drawable.smiley_10));\n        mQQFaceList.add(new QQFace(\"[发怒]\", R.drawable.smiley_11));\n        mQQFaceList.add(new QQFace(\"[调皮]\", R.drawable.smiley_12));\n        mQQFaceList.add(new QQFace(\"[呲牙]\", R.drawable.smiley_13));\n        mQQFaceList.add(new QQFace(\"[惊讶]\", R.drawable.smiley_14));\n        mQQFaceList.add(new QQFace(\"[难过]\", R.drawable.smiley_15));\n        mQQFaceList.add(new QQFace(\"[酷]\", R.drawable.smiley_16));\n        mQQFaceList.add(new QQFace(\"[冷汗]\", R.drawable.smiley_17));\n        mQQFaceList.add(new QQFace(\"[抓狂]\", R.drawable.smiley_18));\n        mQQFaceList.add(new QQFace(\"[吐]\", R.drawable.smiley_19));\n        mQQFaceList.add(new QQFace(\"[偷笑]\", R.drawable.smiley_20));\n        mQQFaceList.add(new QQFace(\"[可爱]\", R.drawable.smiley_21));\n        mQQFaceList.add(new QQFace(\"[白眼]\", R.drawable.smiley_22));\n        mQQFaceList.add(new QQFace(\"[傲慢]\", R.drawable.smiley_23));\n        mQQFaceList.add(new QQFace(\"[饥饿]\", R.drawable.smiley_24));\n        mQQFaceList.add(new QQFace(\"[困]\", R.drawable.smiley_25));\n        mQQFaceList.add(new QQFace(\"[惊恐]\", R.drawable.smiley_26));\n        mQQFaceList.add(new QQFace(\"[流汗]\", R.drawable.smiley_27));\n        mQQFaceList.add(new QQFace(\"[憨笑]\", R.drawable.smiley_28));\n        mQQFaceList.add(new QQFace(\"[大兵]\", R.drawable.smiley_29));\n        mQQFaceList.add(new QQFace(\"[奋斗]\", R.drawable.smiley_30));\n        mQQFaceList.add(new QQFace(\"[咒骂]\", R.drawable.smiley_31));\n        mQQFaceList.add(new QQFace(\"[疑问]\", R.drawable.smiley_32));\n        mQQFaceList.add(new QQFace(\"[嘘]\", R.drawable.smiley_33));\n        mQQFaceList.add(new QQFace(\"[晕]\", R.drawable.smiley_34));\n        mQQFaceList.add(new QQFace(\"[折磨]\", R.drawable.smiley_35));\n        mQQFaceList.add(new QQFace(\"[衰]\", R.drawable.smiley_36));\n        mQQFaceList.add(new QQFace(\"[骷髅]\", R.drawable.smiley_37));\n        mQQFaceList.add(new QQFace(\"[敲打]\", R.drawable.smiley_38));\n        mQQFaceList.add(new QQFace(\"[再见]\", R.drawable.smiley_39));\n        mQQFaceList.add(new QQFace(\"[擦汗]\", R.drawable.smiley_40));\n        mQQFaceList.add(new QQFace(\"[抠鼻]\", R.drawable.smiley_41));\n        mQQFaceList.add(new QQFace(\"[鼓掌]\", R.drawable.smiley_42));\n        mQQFaceList.add(new QQFace(\"[糗大了]\", R.drawable.smiley_43));\n        mQQFaceList.add(new QQFace(\"[坏笑]\", R.drawable.smiley_44));\n        mQQFaceList.add(new QQFace(\"[左哼哼]\", R.drawable.smiley_45));\n        mQQFaceList.add(new QQFace(\"[右哼哼]\", R.drawable.smiley_46));\n        mQQFaceList.add(new QQFace(\"[哈欠]\", R.drawable.smiley_47));\n        mQQFaceList.add(new QQFace(\"[鄙视]\", R.drawable.smiley_48));\n        mQQFaceList.add(new QQFace(\"[委屈]\", R.drawable.smiley_49));\n        mQQFaceList.add(new QQFace(\"[快哭了]\", R.drawable.smiley_50));\n        mQQFaceList.add(new QQFace(\"[阴险]\", R.drawable.smiley_51));\n        mQQFaceList.add(new QQFace(\"[亲亲]\", R.drawable.smiley_52));\n        mQQFaceList.add(new QQFace(\"[吓]\", R.drawable.smiley_53));\n        mQQFaceList.add(new QQFace(\"[可怜]\", R.drawable.smiley_54));\n        mQQFaceList.add(new QQFace(\"[菜刀]\", R.drawable.smiley_55));\n        mQQFaceList.add(new QQFace(\"[西瓜]\", R.drawable.smiley_56));\n        mQQFaceList.add(new QQFace(\"[啤酒]\", R.drawable.smiley_57));\n        mQQFaceList.add(new QQFace(\"[篮球]\", R.drawable.smiley_58));\n        mQQFaceList.add(new QQFace(\"[乒乓]\", R.drawable.smiley_59));\n        mQQFaceList.add(new QQFace(\"[咖啡]\", R.drawable.smiley_60));\n        mQQFaceList.add(new QQFace(\"[饭]\", R.drawable.smiley_61));\n        mQQFaceList.add(new QQFace(\"[猪头]\", R.drawable.smiley_62));\n        mQQFaceList.add(new QQFace(\"[玫瑰]\", R.drawable.smiley_63));\n        mQQFaceList.add(new QQFace(\"[凋谢]\", R.drawable.smiley_64));\n        mQQFaceList.add(new QQFace(\"[示爱]\", R.drawable.smiley_65));\n        mQQFaceList.add(new QQFace(\"[爱心]\", R.drawable.smiley_66));\n        mQQFaceList.add(new QQFace(\"[心碎]\", R.drawable.smiley_67));\n        mQQFaceList.add(new QQFace(\"[蛋糕]\", R.drawable.smiley_68));\n        mQQFaceList.add(new QQFace(\"[闪电]\", R.drawable.smiley_69));\n        mQQFaceList.add(new QQFace(\"[炸弹]\", R.drawable.smiley_70));\n        mQQFaceList.add(new QQFace(\"[刀]\", R.drawable.smiley_71));\n        mQQFaceList.add(new QQFace(\"[足球]\", R.drawable.smiley_72));\n        mQQFaceList.add(new QQFace(\"[瓢虫]\", R.drawable.smiley_73));\n        mQQFaceList.add(new QQFace(\"[便便]\", R.drawable.smiley_74));\n        mQQFaceList.add(new QQFace(\"[月亮]\", R.drawable.smiley_75));\n        mQQFaceList.add(new QQFace(\"[太阳]\", R.drawable.smiley_76));\n        mQQFaceList.add(new QQFace(\"[礼物]\", R.drawable.smiley_77));\n        mQQFaceList.add(new QQFace(\"[拥抱]\", R.drawable.smiley_78));\n        mQQFaceList.add(new QQFace(\"[强]\", R.drawable.smiley_79));\n        mQQFaceList.add(new QQFace(\"[弱]\", R.drawable.smiley_80));\n        mQQFaceList.add(new QQFace(\"[握手]\", R.drawable.smiley_81));\n        mQQFaceList.add(new QQFace(\"[胜利]\", R.drawable.smiley_82));\n        mQQFaceList.add(new QQFace(\"[抱拳]\", R.drawable.smiley_83));\n        mQQFaceList.add(new QQFace(\"[勾引]\", R.drawable.smiley_84));\n        mQQFaceList.add(new QQFace(\"[拳头]\", R.drawable.smiley_85));\n        mQQFaceList.add(new QQFace(\"[差劲]\", R.drawable.smiley_86));\n        mQQFaceList.add(new QQFace(\"[爱你]\", R.drawable.smiley_87));\n        mQQFaceList.add(new QQFace(\"[NO]\", R.drawable.smiley_88));\n        mQQFaceList.add(new QQFace(\"[OK]\", R.drawable.smiley_89));\n        mQQFaceList.add(new QQFace(\"[爱情]\", R.drawable.smiley_90));\n        mQQFaceList.add(new QQFace(\"[飞吻]\", R.drawable.smiley_91));\n        mQQFaceList.add(new QQFace(\"[跳跳]\", R.drawable.smiley_92));\n        mQQFaceList.add(new QQFace(\"[发抖]\", R.drawable.smiley_93));\n        mQQFaceList.add(new QQFace(\"[怄火]\", R.drawable.smiley_94));\n        mQQFaceList.add(new QQFace(\"[转圈]\", R.drawable.smiley_95));\n        mQQFaceList.add(new QQFace(\"[磕头]\", R.drawable.smiley_96));\n        mQQFaceList.add(new QQFace(\"[回头]\", R.drawable.smiley_97));\n        mQQFaceList.add(new QQFace(\"[跳绳]\", R.drawable.smiley_98));\n        mQQFaceList.add(new QQFace(\"[挥手]\", R.drawable.smiley_99));\n        mQQFaceList.add(new QQFace(\"[激动]\", R.drawable.smiley_100));\n        mQQFaceList.add(new QQFace(\"[街舞]\", R.drawable.smiley_101));\n        mQQFaceList.add(new QQFace(\"[献吻]\", R.drawable.smiley_102));\n        mQQFaceList.add(new QQFace(\"[左太极]\", R.drawable.smiley_103));\n        mQQFaceList.add(new QQFace(\"[右太极]\", R.drawable.smiley_104));\n\n        for (QQFace face : mQQFaceList) {\n            sQQFaceMap.put(face.name, face.res);\n        }\n\n        mQQFaceFileNameList.put(\"[微笑]\", \"smiley_0\");\n        mQQFaceFileNameList.put(\"[撇嘴]\", \"smiley_1\");\n        mQQFaceFileNameList.put(\"[色]\", \"smiley_2\");\n        mQQFaceFileNameList.put(\"[发呆]\", \"smiley_3\");\n        mQQFaceFileNameList.put(\"[得意]\", \"smiley_4\");\n        mQQFaceFileNameList.put(\"[流泪]\", \"smiley_5\");\n        mQQFaceFileNameList.put(\"[害羞]\", \"smiley_6\");\n        mQQFaceFileNameList.put(\"[闭嘴]\", \"smiley_7\");\n        mQQFaceFileNameList.put(\"[睡]\", \"smiley_8\");\n        mQQFaceFileNameList.put(\"[大哭]\", \"smiley_9\");\n        mQQFaceFileNameList.put(\"[尴尬]\", \"smiley_10\");\n        mQQFaceFileNameList.put(\"[发怒]\", \"smiley_11\");\n        mQQFaceFileNameList.put(\"[调皮]\", \"smiley_12\");\n        mQQFaceFileNameList.put(\"[呲牙]\", \"smiley_13\");\n        mQQFaceFileNameList.put(\"[惊讶]\", \"smiley_14\");\n        mQQFaceFileNameList.put(\"[难过]\", \"smiley_15\");\n        mQQFaceFileNameList.put(\"[酷]\", \"smiley_16\");\n        mQQFaceFileNameList.put(\"[冷汗]\", \"smiley_17\");\n        mQQFaceFileNameList.put(\"[抓狂]\", \"smiley_18\");\n        mQQFaceFileNameList.put(\"[吐]\", \"smiley_19\");\n        mQQFaceFileNameList.put(\"[偷笑]\", \"smiley_20\");\n        mQQFaceFileNameList.put(\"[可爱]\", \"smiley_21\");\n        mQQFaceFileNameList.put(\"[白眼]\", \"smiley_22\");\n        mQQFaceFileNameList.put(\"[傲慢]\", \"smiley_23\");\n        mQQFaceFileNameList.put(\"[饥饿]\", \"smiley_24\");\n        mQQFaceFileNameList.put(\"[困]\", \"smiley_25\");\n        mQQFaceFileNameList.put(\"[惊恐]\", \"smiley_26\");\n        mQQFaceFileNameList.put(\"[流汗]\", \"smiley_27\");\n        mQQFaceFileNameList.put(\"[憨笑]\", \"smiley_28\");\n        mQQFaceFileNameList.put(\"[大兵]\", \"smiley_29\");\n        mQQFaceFileNameList.put(\"[奋斗]\", \"smiley_30\");\n        mQQFaceFileNameList.put(\"[咒骂]\", \"smiley_31\");\n        mQQFaceFileNameList.put(\"[疑问]\", \"smiley_32\");\n        mQQFaceFileNameList.put(\"[嘘]\", \"smiley_33\");\n        mQQFaceFileNameList.put(\"[晕]\", \"smiley_34\");\n        mQQFaceFileNameList.put(\"[折磨]\", \"smiley_35\");\n        mQQFaceFileNameList.put(\"[衰]\", \"smiley_36\");\n        mQQFaceFileNameList.put(\"[骷髅]\", \"smiley_37\");\n        mQQFaceFileNameList.put(\"[敲打]\", \"smiley_38\");\n        mQQFaceFileNameList.put(\"[再见]\", \"smiley_39\");\n        mQQFaceFileNameList.put(\"[擦汗]\", \"smiley_40\");\n        mQQFaceFileNameList.put(\"[抠鼻]\", \"smiley_41\");\n        mQQFaceFileNameList.put(\"[鼓掌]\", \"smiley_42\");\n        mQQFaceFileNameList.put(\"[糗大了]\", \"smiley_43\");\n        mQQFaceFileNameList.put(\"[坏笑]\", \"smiley_44\");\n        mQQFaceFileNameList.put(\"[左哼哼]\", \"smiley_45\");\n        mQQFaceFileNameList.put(\"[右哼哼]\", \"smiley_46\");\n        mQQFaceFileNameList.put(\"[哈欠]\", \"smiley_47\");\n        mQQFaceFileNameList.put(\"[鄙视]\", \"smiley_48\");\n        mQQFaceFileNameList.put(\"[委屈]\", \"smiley_49\");\n        mQQFaceFileNameList.put(\"[快哭了]\", \"smiley_50\");\n        mQQFaceFileNameList.put(\"[阴险]\", \"smiley_51\");\n        mQQFaceFileNameList.put(\"[亲亲]\", \"smiley_52\");\n        mQQFaceFileNameList.put(\"[吓]\", \"smiley_53\");\n        mQQFaceFileNameList.put(\"[可怜]\", \"smiley_54\");\n        mQQFaceFileNameList.put(\"[菜刀]\", \"smiley_55\");\n        mQQFaceFileNameList.put(\"[西瓜]\", \"smiley_56\");\n        mQQFaceFileNameList.put(\"[啤酒]\", \"smiley_57\");\n        mQQFaceFileNameList.put(\"[篮球]\", \"smiley_58\");\n        mQQFaceFileNameList.put(\"[乒乓]\", \"smiley_59\");\n        mQQFaceFileNameList.put(\"[咖啡]\", \"smiley_60\");\n        mQQFaceFileNameList.put(\"[饭]\", \"smiley_61\");\n        mQQFaceFileNameList.put(\"[猪头]\", \"smiley_62\");\n        mQQFaceFileNameList.put(\"[玫瑰]\", \"smiley_63\");\n        mQQFaceFileNameList.put(\"[凋谢]\", \"smiley_64\");\n        mQQFaceFileNameList.put(\"[示爱]\", \"smiley_65\");\n        mQQFaceFileNameList.put(\"[爱心]\", \"smiley_66\");\n        mQQFaceFileNameList.put(\"[心碎]\", \"smiley_67\");\n        mQQFaceFileNameList.put(\"[蛋糕]\", \"smiley_68\");\n        mQQFaceFileNameList.put(\"[闪电]\", \"smiley_69\");\n        mQQFaceFileNameList.put(\"[炸弹]\", \"smiley_70\");\n        mQQFaceFileNameList.put(\"[刀]\", \"smiley_71\");\n        mQQFaceFileNameList.put(\"[足球]\", \"smiley_72\");\n        mQQFaceFileNameList.put(\"[瓢虫]\", \"smiley_73\");\n        mQQFaceFileNameList.put(\"[便便]\", \"smiley_74\");\n        mQQFaceFileNameList.put(\"[月亮]\", \"smiley_75\");\n        mQQFaceFileNameList.put(\"[太阳]\", \"smiley_76\");\n        mQQFaceFileNameList.put(\"[礼物]\", \"smiley_77\");\n        mQQFaceFileNameList.put(\"[拥抱]\", \"smiley_78\");\n        mQQFaceFileNameList.put(\"[强]\", \"smiley_79\");\n        mQQFaceFileNameList.put(\"[弱]\", \"smiley_80\");\n        mQQFaceFileNameList.put(\"[握手]\", \"smiley_81\");\n        mQQFaceFileNameList.put(\"[胜利]\", \"smiley_82\");\n        mQQFaceFileNameList.put(\"[抱拳]\", \"smiley_83\");\n        mQQFaceFileNameList.put(\"[勾引]\", \"smiley_84\");\n        mQQFaceFileNameList.put(\"[拳头]\", \"smiley_85\");\n        mQQFaceFileNameList.put(\"[差劲]\", \"smiley_86\");\n        mQQFaceFileNameList.put(\"[爱你]\", \"smiley_87\");\n        mQQFaceFileNameList.put(\"[NO]\", \"smiley_88\");\n        mQQFaceFileNameList.put(\"[OK]\", \"smiley_89\");\n        mQQFaceFileNameList.put(\"[爱情]\", \"smiley_90\");\n        mQQFaceFileNameList.put(\"[飞吻]\", \"smiley_91\");\n        mQQFaceFileNameList.put(\"[跳跳]\", \"smiley_92\");\n        mQQFaceFileNameList.put(\"[发抖]\", \"smiley_93\");\n        mQQFaceFileNameList.put(\"[怄火]\", \"smiley_94\");\n        mQQFaceFileNameList.put(\"[转圈]\", \"smiley_95\");\n        mQQFaceFileNameList.put(\"[磕头]\", \"smiley_96\");\n        mQQFaceFileNameList.put(\"[回头]\", \"smiley_97\");\n        mQQFaceFileNameList.put(\"[跳绳]\", \"smiley_98\");\n        mQQFaceFileNameList.put(\"[挥手]\", \"smiley_99\");\n        mQQFaceFileNameList.put(\"[激动]\", \"smiley_100\");\n        mQQFaceFileNameList.put(\"[街舞]\", \"smiley_101\");\n        mQQFaceFileNameList.put(\"[献吻]\", \"smiley_102\");\n        mQQFaceFileNameList.put(\"[左太极]\", \"smiley_103\");\n        mQQFaceFileNameList.put(\"[右太极]\", \"smiley_104\");\n\n        sEmojisMap.append(0x00a9, R.drawable.emoji_00a9);\n        sEmojisMap.append(0x00ae, R.drawable.emoji_00ae);\n        sEmojisMap.append(0x203c, R.drawable.emoji_203c);\n        sEmojisMap.append(0x2049, R.drawable.emoji_2049);\n        sEmojisMap.append(0x2122, R.drawable.emoji_2122);\n        sEmojisMap.append(0x2139, R.drawable.emoji_2139);\n        sEmojisMap.append(0x2194, R.drawable.emoji_2194);\n        sEmojisMap.append(0x2195, R.drawable.emoji_2195);\n        sEmojisMap.append(0x2196, R.drawable.emoji_2196);\n        sEmojisMap.append(0x2197, R.drawable.emoji_2197);\n        sEmojisMap.append(0x2198, R.drawable.emoji_2198);\n        sEmojisMap.append(0x2199, R.drawable.emoji_2199);\n        sEmojisMap.append(0x21a9, R.drawable.emoji_21a9);\n        sEmojisMap.append(0x21aa, R.drawable.emoji_21aa);\n        sEmojisMap.append(0x231a, R.drawable.emoji_231a);\n        sEmojisMap.append(0x231b, R.drawable.emoji_231b);\n        sEmojisMap.append(0x23e9, R.drawable.emoji_23e9);\n        sEmojisMap.append(0x23ea, R.drawable.emoji_23ea);\n        sEmojisMap.append(0x23eb, R.drawable.emoji_23eb);\n        sEmojisMap.append(0x23ec, R.drawable.emoji_23ec);\n        sEmojisMap.append(0x23f0, R.drawable.emoji_23f0);\n        sEmojisMap.append(0x23f3, R.drawable.emoji_23f3);\n        sEmojisMap.append(0x24c2, R.drawable.emoji_24c2);\n        sEmojisMap.append(0x25aa, R.drawable.emoji_25aa);\n        sEmojisMap.append(0x25ab, R.drawable.emoji_25ab);\n        sEmojisMap.append(0x25b6, R.drawable.emoji_25b6);\n        sEmojisMap.append(0x25c0, R.drawable.emoji_25c0);\n        sEmojisMap.append(0x25fb, R.drawable.emoji_25fb);\n        sEmojisMap.append(0x25fc, R.drawable.emoji_25fc);\n        sEmojisMap.append(0x25fd, R.drawable.emoji_25fd);\n        sEmojisMap.append(0x25fe, R.drawable.emoji_25fe);\n        sEmojisMap.append(0x2600, R.drawable.emoji_2600);\n        sEmojisMap.append(0x2601, R.drawable.emoji_2601);\n        sEmojisMap.append(0x260e, R.drawable.emoji_260e);\n        sEmojisMap.append(0x2611, R.drawable.emoji_2611);\n        sEmojisMap.append(0x2614, R.drawable.emoji_2614);\n        sEmojisMap.append(0x2615, R.drawable.emoji_2615);\n        sEmojisMap.append(0x261d, R.drawable.emoji_261d);\n        sEmojisMap.append(0x263a, R.drawable.emoji_263a);\n        sEmojisMap.append(0x2648, R.drawable.emoji_2648);\n        sEmojisMap.append(0x2649, R.drawable.emoji_2649);\n        sEmojisMap.append(0x264a, R.drawable.emoji_264a);\n        sEmojisMap.append(0x264b, R.drawable.emoji_264b);\n        sEmojisMap.append(0x264c, R.drawable.emoji_264c);\n        sEmojisMap.append(0x264d, R.drawable.emoji_264d);\n        sEmojisMap.append(0x264e, R.drawable.emoji_264e);\n        sEmojisMap.append(0x264f, R.drawable.emoji_264f);\n        sEmojisMap.append(0x2650, R.drawable.emoji_2650);\n        sEmojisMap.append(0x2651, R.drawable.emoji_2651);\n        sEmojisMap.append(0x2652, R.drawable.emoji_2652);\n        sEmojisMap.append(0x2653, R.drawable.emoji_2653);\n        sEmojisMap.append(0x2660, R.drawable.emoji_2660);\n        sEmojisMap.append(0x2663, R.drawable.emoji_2663);\n        sEmojisMap.append(0x2665, R.drawable.emoji_2665);\n        sEmojisMap.append(0x2666, R.drawable.emoji_2666);\n        sEmojisMap.append(0x2668, R.drawable.emoji_2668);\n        sEmojisMap.append(0x267b, R.drawable.emoji_267b);\n        sEmojisMap.append(0x267f, R.drawable.emoji_267f);\n        sEmojisMap.append(0x2693, R.drawable.emoji_2693);\n        sEmojisMap.append(0x26a0, R.drawable.emoji_26a0);\n        sEmojisMap.append(0x26a1, R.drawable.emoji_26a1);\n        sEmojisMap.append(0x26aa, R.drawable.emoji_26aa);\n        sEmojisMap.append(0x26ab, R.drawable.emoji_26ab);\n        sEmojisMap.append(0x26bd, R.drawable.emoji_26bd);\n        sEmojisMap.append(0x26be, R.drawable.emoji_26be);\n        sEmojisMap.append(0x26c4, R.drawable.emoji_26c4);\n        sEmojisMap.append(0x26c5, R.drawable.emoji_26c5);\n        sEmojisMap.append(0x26ce, R.drawable.emoji_26ce);\n        sEmojisMap.append(0x26d4, R.drawable.emoji_26d4);\n        sEmojisMap.append(0x26ea, R.drawable.emoji_26ea);\n        sEmojisMap.append(0x26f2, R.drawable.emoji_26f2);\n        sEmojisMap.append(0x26f3, R.drawable.emoji_26f3);\n        sEmojisMap.append(0x26f5, R.drawable.emoji_26f5);\n        sEmojisMap.append(0x26fa, R.drawable.emoji_26fa);\n        sEmojisMap.append(0x26fd, R.drawable.emoji_26fd);\n        sEmojisMap.append(0x2702, R.drawable.emoji_2702);\n        sEmojisMap.append(0x2705, R.drawable.emoji_2705);\n        sEmojisMap.append(0x2708, R.drawable.emoji_2708);\n        sEmojisMap.append(0x2709, R.drawable.emoji_2709);\n        sEmojisMap.append(0x270a, R.drawable.emoji_270a);\n        sEmojisMap.append(0x270b, R.drawable.emoji_270b);\n        sEmojisMap.append(0x270c, R.drawable.emoji_270c);\n        sEmojisMap.append(0x270f, R.drawable.emoji_270f);\n        sEmojisMap.append(0x2712, R.drawable.emoji_2712);\n        sEmojisMap.append(0x2714, R.drawable.emoji_2714);\n        sEmojisMap.append(0x2716, R.drawable.emoji_2716);\n        sEmojisMap.append(0x2728, R.drawable.emoji_2728);\n        sEmojisMap.append(0x2733, R.drawable.emoji_2733);\n        sEmojisMap.append(0x2734, R.drawable.emoji_2734);\n        sEmojisMap.append(0x2744, R.drawable.emoji_2744);\n        sEmojisMap.append(0x2747, R.drawable.emoji_2747);\n        sEmojisMap.append(0x274c, R.drawable.emoji_274c);\n        sEmojisMap.append(0x274e, R.drawable.emoji_274e);\n        sEmojisMap.append(0x2753, R.drawable.emoji_2753);\n        sEmojisMap.append(0x2754, R.drawable.emoji_2754);\n        sEmojisMap.append(0x2755, R.drawable.emoji_2755);\n        sEmojisMap.append(0x2757, R.drawable.emoji_2757);\n        sEmojisMap.append(0x2764, R.drawable.emoji_2764);\n        sEmojisMap.append(0x2795, R.drawable.emoji_2795);\n        sEmojisMap.append(0x2796, R.drawable.emoji_2796);\n        sEmojisMap.append(0x2797, R.drawable.emoji_2797);\n        sEmojisMap.append(0x27a1, R.drawable.emoji_27a1);\n        sEmojisMap.append(0x27b0, R.drawable.emoji_27b0);\n        sEmojisMap.append(0x27bf, R.drawable.emoji_27bf);\n        sEmojisMap.append(0x2934, R.drawable.emoji_2934);\n        sEmojisMap.append(0x2935, R.drawable.emoji_2935);\n        sEmojisMap.append(0x2b05, R.drawable.emoji_2b05);\n        sEmojisMap.append(0x2b06, R.drawable.emoji_2b06);\n        sEmojisMap.append(0x2b07, R.drawable.emoji_2b07);\n        sEmojisMap.append(0x2b1b, R.drawable.emoji_2b1b);\n        sEmojisMap.append(0x2b1c, R.drawable.emoji_2b1c);\n        sEmojisMap.append(0x2b50, R.drawable.emoji_2b50);\n        sEmojisMap.append(0x2b55, R.drawable.emoji_2b55);\n        sEmojisMap.append(0x3030, R.drawable.emoji_3030);\n        sEmojisMap.append(0x303d, R.drawable.emoji_303d);\n        sEmojisMap.append(0x3297, R.drawable.emoji_3297);\n        sEmojisMap.append(0x3299, R.drawable.emoji_3299);\n        sEmojisMap.append(0x1f004, R.drawable.emoji_1f004);\n        sEmojisMap.append(0x1f0cf, R.drawable.emoji_1f0cf);\n        sEmojisMap.append(0x1f170, R.drawable.emoji_1f170);\n        sEmojisMap.append(0x1f171, R.drawable.emoji_1f171);\n        sEmojisMap.append(0x1f17e, R.drawable.emoji_1f17e);\n        sEmojisMap.append(0x1f17f, R.drawable.emoji_1f17f);\n        sEmojisMap.append(0x1f18e, R.drawable.emoji_1f18e);\n        sEmojisMap.append(0x1f191, R.drawable.emoji_1f191);\n        sEmojisMap.append(0x1f192, R.drawable.emoji_1f192);\n        sEmojisMap.append(0x1f193, R.drawable.emoji_1f193);\n        sEmojisMap.append(0x1f194, R.drawable.emoji_1f194);\n        sEmojisMap.append(0x1f195, R.drawable.emoji_1f195);\n        sEmojisMap.append(0x1f196, R.drawable.emoji_1f196);\n        sEmojisMap.append(0x1f197, R.drawable.emoji_1f197);\n        sEmojisMap.append(0x1f198, R.drawable.emoji_1f198);\n        sEmojisMap.append(0x1f199, R.drawable.emoji_1f199);\n        sEmojisMap.append(0x1f19a, R.drawable.emoji_1f19a);\n        sEmojisMap.append(0x1f201, R.drawable.emoji_1f201);\n        sEmojisMap.append(0x1f202, R.drawable.emoji_1f202);\n        sEmojisMap.append(0x1f21a, R.drawable.emoji_1f21a);\n        sEmojisMap.append(0x1f22f, R.drawable.emoji_1f22f);\n        sEmojisMap.append(0x1f232, R.drawable.emoji_1f232);\n        sEmojisMap.append(0x1f233, R.drawable.emoji_1f233);\n        sEmojisMap.append(0x1f234, R.drawable.emoji_1f234);\n        sEmojisMap.append(0x1f235, R.drawable.emoji_1f235);\n        sEmojisMap.append(0x1f236, R.drawable.emoji_1f236);\n        sEmojisMap.append(0x1f237, R.drawable.emoji_1f237);\n        sEmojisMap.append(0x1f238, R.drawable.emoji_1f238);\n        sEmojisMap.append(0x1f239, R.drawable.emoji_1f239);\n        sEmojisMap.append(0x1f23a, R.drawable.emoji_1f23a);\n        sEmojisMap.append(0x1f250, R.drawable.emoji_1f250);\n        sEmojisMap.append(0x1f251, R.drawable.emoji_1f251);\n        sEmojisMap.append(0x1f300, R.drawable.emoji_1f300);\n        sEmojisMap.append(0x1f301, R.drawable.emoji_1f301);\n        sEmojisMap.append(0x1f302, R.drawable.emoji_1f302);\n        sEmojisMap.append(0x1f303, R.drawable.emoji_1f303);\n        sEmojisMap.append(0x1f304, R.drawable.emoji_1f304);\n        sEmojisMap.append(0x1f305, R.drawable.emoji_1f305);\n        sEmojisMap.append(0x1f306, R.drawable.emoji_1f306);\n        sEmojisMap.append(0x1f307, R.drawable.emoji_1f307);\n        sEmojisMap.append(0x1f308, R.drawable.emoji_1f308);\n        sEmojisMap.append(0x1f309, R.drawable.emoji_1f309);\n        sEmojisMap.append(0x1f30a, R.drawable.emoji_1f30a);\n        sEmojisMap.append(0x1f30b, R.drawable.emoji_1f30b);\n        sEmojisMap.append(0x1f30c, R.drawable.emoji_1f30c);\n        sEmojisMap.append(0x1f30d, R.drawable.emoji_1f30d);\n        sEmojisMap.append(0x1f30e, R.drawable.emoji_1f30e);\n        sEmojisMap.append(0x1f30f, R.drawable.emoji_1f30f);\n        sEmojisMap.append(0x1f310, R.drawable.emoji_1f310);\n        sEmojisMap.append(0x1f311, R.drawable.emoji_1f311);\n        sEmojisMap.append(0x1f312, R.drawable.emoji_1f312);\n        sEmojisMap.append(0x1f313, R.drawable.emoji_1f313);\n        sEmojisMap.append(0x1f314, R.drawable.emoji_1f314);\n        sEmojisMap.append(0x1f315, R.drawable.emoji_1f315);\n        sEmojisMap.append(0x1f316, R.drawable.emoji_1f316);\n        sEmojisMap.append(0x1f317, R.drawable.emoji_1f317);\n        sEmojisMap.append(0x1f318, R.drawable.emoji_1f318);\n        sEmojisMap.append(0x1f319, R.drawable.emoji_1f319);\n        sEmojisMap.append(0x1f31a, R.drawable.emoji_1f31a);\n        sEmojisMap.append(0x1f31b, R.drawable.emoji_1f31b);\n        sEmojisMap.append(0x1f31c, R.drawable.emoji_1f31c);\n        sEmojisMap.append(0x1f31d, R.drawable.emoji_1f31d);\n        sEmojisMap.append(0x1f31e, R.drawable.emoji_1f31e);\n        sEmojisMap.append(0x1f31f, R.drawable.emoji_1f31f);\n        sEmojisMap.append(0x1f320, R.drawable.emoji_1f303);\n        sEmojisMap.append(0x1f330, R.drawable.emoji_1f330);\n        sEmojisMap.append(0x1f331, R.drawable.emoji_1f331);\n        sEmojisMap.append(0x1f332, R.drawable.emoji_1f332);\n        sEmojisMap.append(0x1f333, R.drawable.emoji_1f333);\n        sEmojisMap.append(0x1f334, R.drawable.emoji_1f334);\n        sEmojisMap.append(0x1f335, R.drawable.emoji_1f335);\n        sEmojisMap.append(0x1f337, R.drawable.emoji_1f337);\n        sEmojisMap.append(0x1f338, R.drawable.emoji_1f338);\n        sEmojisMap.append(0x1f339, R.drawable.emoji_1f339);\n        sEmojisMap.append(0x1f33a, R.drawable.emoji_1f33a);\n        sEmojisMap.append(0x1f33b, R.drawable.emoji_1f33b);\n        sEmojisMap.append(0x1f33c, R.drawable.emoji_1f33c);\n        sEmojisMap.append(0x1f33d, R.drawable.emoji_1f33d);\n        sEmojisMap.append(0x1f33e, R.drawable.emoji_1f33e);\n        sEmojisMap.append(0x1f33f, R.drawable.emoji_1f33f);\n        sEmojisMap.append(0x1f340, R.drawable.emoji_1f340);\n        sEmojisMap.append(0x1f341, R.drawable.emoji_1f341);\n        sEmojisMap.append(0x1f342, R.drawable.emoji_1f342);\n        sEmojisMap.append(0x1f343, R.drawable.emoji_1f343);\n        sEmojisMap.append(0x1f344, R.drawable.emoji_1f344);\n        sEmojisMap.append(0x1f345, R.drawable.emoji_1f345);\n        sEmojisMap.append(0x1f346, R.drawable.emoji_1f346);\n        sEmojisMap.append(0x1f347, R.drawable.emoji_1f347);\n        sEmojisMap.append(0x1f348, R.drawable.emoji_1f348);\n        sEmojisMap.append(0x1f349, R.drawable.emoji_1f349);\n        sEmojisMap.append(0x1f34a, R.drawable.emoji_1f34a);\n        sEmojisMap.append(0x1f34b, R.drawable.emoji_1f34b);\n        sEmojisMap.append(0x1f34c, R.drawable.emoji_1f34c);\n        sEmojisMap.append(0x1f34d, R.drawable.emoji_1f34d);\n        sEmojisMap.append(0x1f34e, R.drawable.emoji_1f34e);\n        sEmojisMap.append(0x1f34f, R.drawable.emoji_1f34f);\n        sEmojisMap.append(0x1f350, R.drawable.emoji_1f350);\n        sEmojisMap.append(0x1f351, R.drawable.emoji_1f351);\n        sEmojisMap.append(0x1f352, R.drawable.emoji_1f352);\n        sEmojisMap.append(0x1f353, R.drawable.emoji_1f353);\n        sEmojisMap.append(0x1f354, R.drawable.emoji_1f354);\n        sEmojisMap.append(0x1f355, R.drawable.emoji_1f355);\n        sEmojisMap.append(0x1f356, R.drawable.emoji_1f356);\n        sEmojisMap.append(0x1f357, R.drawable.emoji_1f357);\n        sEmojisMap.append(0x1f358, R.drawable.emoji_1f358);\n        sEmojisMap.append(0x1f359, R.drawable.emoji_1f359);\n        sEmojisMap.append(0x1f35a, R.drawable.emoji_1f35a);\n        sEmojisMap.append(0x1f35b, R.drawable.emoji_1f35b);\n        sEmojisMap.append(0x1f35c, R.drawable.emoji_1f35c);\n        sEmojisMap.append(0x1f35d, R.drawable.emoji_1f35d);\n        sEmojisMap.append(0x1f35e, R.drawable.emoji_1f35e);\n        sEmojisMap.append(0x1f35f, R.drawable.emoji_1f35f);\n        sEmojisMap.append(0x1f360, R.drawable.emoji_1f360);\n        sEmojisMap.append(0x1f361, R.drawable.emoji_1f361);\n        sEmojisMap.append(0x1f362, R.drawable.emoji_1f362);\n        sEmojisMap.append(0x1f363, R.drawable.emoji_1f363);\n        sEmojisMap.append(0x1f364, R.drawable.emoji_1f364);\n        sEmojisMap.append(0x1f365, R.drawable.emoji_1f365);\n        sEmojisMap.append(0x1f366, R.drawable.emoji_1f366);\n        sEmojisMap.append(0x1f367, R.drawable.emoji_1f367);\n        sEmojisMap.append(0x1f368, R.drawable.emoji_1f368);\n        sEmojisMap.append(0x1f369, R.drawable.emoji_1f369);\n        sEmojisMap.append(0x1f36a, R.drawable.emoji_1f36a);\n        sEmojisMap.append(0x1f36b, R.drawable.emoji_1f36b);\n        sEmojisMap.append(0x1f36c, R.drawable.emoji_1f36c);\n        sEmojisMap.append(0x1f36d, R.drawable.emoji_1f36d);\n        sEmojisMap.append(0x1f36e, R.drawable.emoji_1f36e);\n        sEmojisMap.append(0x1f36f, R.drawable.emoji_1f36f);\n        sEmojisMap.append(0x1f370, R.drawable.emoji_1f370);\n        sEmojisMap.append(0x1f371, R.drawable.emoji_1f371);\n        sEmojisMap.append(0x1f372, R.drawable.emoji_1f372);\n        sEmojisMap.append(0x1f373, R.drawable.emoji_1f373);\n        sEmojisMap.append(0x1f374, R.drawable.emoji_1f374);\n        sEmojisMap.append(0x1f375, R.drawable.emoji_1f375);\n        sEmojisMap.append(0x1f376, R.drawable.emoji_1f376);\n        sEmojisMap.append(0x1f377, R.drawable.emoji_1f377);\n        sEmojisMap.append(0x1f378, R.drawable.emoji_1f378);\n        sEmojisMap.append(0x1f379, R.drawable.emoji_1f379);\n        sEmojisMap.append(0x1f37a, R.drawable.emoji_1f37a);\n        sEmojisMap.append(0x1f37b, R.drawable.emoji_1f37b);\n        sEmojisMap.append(0x1f37c, R.drawable.emoji_1f37c);\n        sEmojisMap.append(0x1f380, R.drawable.emoji_1f380);\n        sEmojisMap.append(0x1f381, R.drawable.emoji_1f381);\n        sEmojisMap.append(0x1f382, R.drawable.emoji_1f382);\n        sEmojisMap.append(0x1f383, R.drawable.emoji_1f383);\n        sEmojisMap.append(0x1f384, R.drawable.emoji_1f384);\n        sEmojisMap.append(0x1f385, R.drawable.emoji_1f385);\n        sEmojisMap.append(0x1f386, R.drawable.emoji_1f386);\n        sEmojisMap.append(0x1f387, R.drawable.emoji_1f387);\n        sEmojisMap.append(0x1f388, R.drawable.emoji_1f388);\n        sEmojisMap.append(0x1f389, R.drawable.emoji_1f389);\n        sEmojisMap.append(0x1f38a, R.drawable.emoji_1f38a);\n        sEmojisMap.append(0x1f38b, R.drawable.emoji_1f38b);\n        sEmojisMap.append(0x1f38c, R.drawable.emoji_1f38c);\n        sEmojisMap.append(0x1f38d, R.drawable.emoji_1f38d);\n        sEmojisMap.append(0x1f38e, R.drawable.emoji_1f38e);\n        sEmojisMap.append(0x1f38f, R.drawable.emoji_1f38f);\n        sEmojisMap.append(0x1f390, R.drawable.emoji_1f390);\n        sEmojisMap.append(0x1f391, R.drawable.emoji_1f391);\n        sEmojisMap.append(0x1f392, R.drawable.emoji_1f392);\n        sEmojisMap.append(0x1f393, R.drawable.emoji_1f393);\n        sEmojisMap.append(0x1f3a0, R.drawable.emoji_1f3a0);\n        sEmojisMap.append(0x1f3a1, R.drawable.emoji_1f3a1);\n        sEmojisMap.append(0x1f3a2, R.drawable.emoji_1f3a2);\n        sEmojisMap.append(0x1f3a3, R.drawable.emoji_1f3a3);\n        sEmojisMap.append(0x1f3a4, R.drawable.emoji_1f3a4);\n        sEmojisMap.append(0x1f3a5, R.drawable.emoji_1f3a5);\n        sEmojisMap.append(0x1f3a6, R.drawable.emoji_1f3a6);\n        sEmojisMap.append(0x1f3a7, R.drawable.emoji_1f3a7);\n        sEmojisMap.append(0x1f3a8, R.drawable.emoji_1f3a8);\n        sEmojisMap.append(0x1f3a9, R.drawable.emoji_1f3a9);\n        sEmojisMap.append(0x1f3aa, R.drawable.emoji_1f3aa);\n        sEmojisMap.append(0x1f3ab, R.drawable.emoji_1f3ab);\n        sEmojisMap.append(0x1f3ac, R.drawable.emoji_1f3ac);\n        sEmojisMap.append(0x1f3ad, R.drawable.emoji_1f3ad);\n        sEmojisMap.append(0x1f3ae, R.drawable.emoji_1f3ae);\n        sEmojisMap.append(0x1f3af, R.drawable.emoji_1f3af);\n        sEmojisMap.append(0x1f3b0, R.drawable.emoji_1f3b0);\n        sEmojisMap.append(0x1f3b1, R.drawable.emoji_1f3b1);\n        sEmojisMap.append(0x1f3b2, R.drawable.emoji_1f3b2);\n        sEmojisMap.append(0x1f3b3, R.drawable.emoji_1f3b3);\n        sEmojisMap.append(0x1f3b4, R.drawable.emoji_1f3b4);\n        sEmojisMap.append(0x1f3b5, R.drawable.emoji_1f3b5);\n        sEmojisMap.append(0x1f3b6, R.drawable.emoji_1f3b6);\n        sEmojisMap.append(0x1f3b7, R.drawable.emoji_1f3b7);\n        sEmojisMap.append(0x1f3b8, R.drawable.emoji_1f3b8);\n        sEmojisMap.append(0x1f3b9, R.drawable.emoji_1f3b9);\n        sEmojisMap.append(0x1f3ba, R.drawable.emoji_1f3ba);\n        sEmojisMap.append(0x1f3bb, R.drawable.emoji_1f3bb);\n        sEmojisMap.append(0x1f3bc, R.drawable.emoji_1f3bc);\n        sEmojisMap.append(0x1f3bd, R.drawable.emoji_1f3bd);\n        sEmojisMap.append(0x1f3be, R.drawable.emoji_1f3be);\n        sEmojisMap.append(0x1f3bf, R.drawable.emoji_1f3bf);\n        sEmojisMap.append(0x1f3c0, R.drawable.emoji_1f3c0);\n        sEmojisMap.append(0x1f3c1, R.drawable.emoji_1f3c1);\n        sEmojisMap.append(0x1f3c2, R.drawable.emoji_1f3c2);\n        sEmojisMap.append(0x1f3c3, R.drawable.emoji_1f3c3);\n        sEmojisMap.append(0x1f3c4, R.drawable.emoji_1f3c4);\n        sEmojisMap.append(0x1f3c6, R.drawable.emoji_1f3c6);\n        sEmojisMap.append(0x1f3c7, R.drawable.emoji_1f3c7);\n        sEmojisMap.append(0x1f3c8, R.drawable.emoji_1f3c8);\n        sEmojisMap.append(0x1f3c9, R.drawable.emoji_1f3c9);\n        sEmojisMap.append(0x1f3ca, R.drawable.emoji_1f3ca);\n        sEmojisMap.append(0x1f3e0, R.drawable.emoji_1f3e0);\n        sEmojisMap.append(0x1f3e1, R.drawable.emoji_1f3e1);\n        sEmojisMap.append(0x1f3e2, R.drawable.emoji_1f3e2);\n        sEmojisMap.append(0x1f3e3, R.drawable.emoji_1f3e3);\n        sEmojisMap.append(0x1f3e4, R.drawable.emoji_1f3e4);\n        sEmojisMap.append(0x1f3e5, R.drawable.emoji_1f3e5);\n        sEmojisMap.append(0x1f3e6, R.drawable.emoji_1f3e6);\n        sEmojisMap.append(0x1f3e7, R.drawable.emoji_1f3e7);\n        sEmojisMap.append(0x1f3e8, R.drawable.emoji_1f3e8);\n        sEmojisMap.append(0x1f3e9, R.drawable.emoji_1f3e9);\n        sEmojisMap.append(0x1f3ea, R.drawable.emoji_1f3ea);\n        sEmojisMap.append(0x1f3eb, R.drawable.emoji_1f3eb);\n        sEmojisMap.append(0x1f3ec, R.drawable.emoji_1f3ec);\n        sEmojisMap.append(0x1f3ed, R.drawable.emoji_1f3ed);\n        sEmojisMap.append(0x1f3ee, R.drawable.emoji_1f3ee);\n        sEmojisMap.append(0x1f3ef, R.drawable.emoji_1f3ef);\n        sEmojisMap.append(0x1f3f0, R.drawable.emoji_1f3f0);\n        sEmojisMap.append(0x1f400, R.drawable.emoji_1f400);\n        sEmojisMap.append(0x1f401, R.drawable.emoji_1f401);\n        sEmojisMap.append(0x1f402, R.drawable.emoji_1f402);\n        sEmojisMap.append(0x1f403, R.drawable.emoji_1f403);\n        sEmojisMap.append(0x1f404, R.drawable.emoji_1f404);\n        sEmojisMap.append(0x1f405, R.drawable.emoji_1f405);\n        sEmojisMap.append(0x1f406, R.drawable.emoji_1f406);\n        sEmojisMap.append(0x1f407, R.drawable.emoji_1f407);\n        sEmojisMap.append(0x1f408, R.drawable.emoji_1f408);\n        sEmojisMap.append(0x1f409, R.drawable.emoji_1f409);\n        sEmojisMap.append(0x1f40a, R.drawable.emoji_1f40a);\n        sEmojisMap.append(0x1f40b, R.drawable.emoji_1f40b);\n        sEmojisMap.append(0x1f40c, R.drawable.emoji_1f40c);\n        sEmojisMap.append(0x1f40d, R.drawable.emoji_1f40d);\n        sEmojisMap.append(0x1f40e, R.drawable.emoji_1f40e);\n        sEmojisMap.append(0x1f40f, R.drawable.emoji_1f40f);\n        sEmojisMap.append(0x1f410, R.drawable.emoji_1f410);\n        sEmojisMap.append(0x1f411, R.drawable.emoji_1f411);\n        sEmojisMap.append(0x1f412, R.drawable.emoji_1f412);\n        sEmojisMap.append(0x1f413, R.drawable.emoji_1f413);\n        sEmojisMap.append(0x1f414, R.drawable.emoji_1f414);\n        sEmojisMap.append(0x1f415, R.drawable.emoji_1f415);\n        sEmojisMap.append(0x1f416, R.drawable.emoji_1f416);\n        sEmojisMap.append(0x1f417, R.drawable.emoji_1f417);\n        sEmojisMap.append(0x1f418, R.drawable.emoji_1f418);\n        sEmojisMap.append(0x1f419, R.drawable.emoji_1f419);\n        sEmojisMap.append(0x1f41a, R.drawable.emoji_1f41a);\n        sEmojisMap.append(0x1f41b, R.drawable.emoji_1f41b);\n        sEmojisMap.append(0x1f41c, R.drawable.emoji_1f41c);\n        sEmojisMap.append(0x1f41d, R.drawable.emoji_1f41d);\n        sEmojisMap.append(0x1f41e, R.drawable.emoji_1f41e);\n        sEmojisMap.append(0x1f41f, R.drawable.emoji_1f41f);\n        sEmojisMap.append(0x1f420, R.drawable.emoji_1f420);\n        sEmojisMap.append(0x1f421, R.drawable.emoji_1f421);\n        sEmojisMap.append(0x1f422, R.drawable.emoji_1f422);\n        sEmojisMap.append(0x1f423, R.drawable.emoji_1f423);\n        sEmojisMap.append(0x1f424, R.drawable.emoji_1f424);\n        sEmojisMap.append(0x1f425, R.drawable.emoji_1f425);\n        sEmojisMap.append(0x1f426, R.drawable.emoji_1f426);\n        sEmojisMap.append(0x1f427, R.drawable.emoji_1f427);\n        sEmojisMap.append(0x1f428, R.drawable.emoji_1f428);\n        sEmojisMap.append(0x1f429, R.drawable.emoji_1f429);\n        sEmojisMap.append(0x1f42a, R.drawable.emoji_1f42a);\n        sEmojisMap.append(0x1f42b, R.drawable.emoji_1f42b);\n        sEmojisMap.append(0x1f42c, R.drawable.emoji_1f42c);\n        sEmojisMap.append(0x1f42d, R.drawable.emoji_1f42d);\n        sEmojisMap.append(0x1f42e, R.drawable.emoji_1f42e);\n        sEmojisMap.append(0x1f42f, R.drawable.emoji_1f42f);\n        sEmojisMap.append(0x1f430, R.drawable.emoji_1f430);\n        sEmojisMap.append(0x1f431, R.drawable.emoji_1f431);\n        sEmojisMap.append(0x1f432, R.drawable.emoji_1f432);\n        sEmojisMap.append(0x1f433, R.drawable.emoji_1f433);\n        sEmojisMap.append(0x1f434, R.drawable.emoji_1f434);\n        sEmojisMap.append(0x1f435, R.drawable.emoji_1f435);\n        sEmojisMap.append(0x1f436, R.drawable.emoji_1f436);\n        sEmojisMap.append(0x1f437, R.drawable.emoji_1f437);\n        sEmojisMap.append(0x1f438, R.drawable.emoji_1f438);\n        sEmojisMap.append(0x1f439, R.drawable.emoji_1f439);\n        sEmojisMap.append(0x1f43a, R.drawable.emoji_1f43a);\n        sEmojisMap.append(0x1f43b, R.drawable.emoji_1f43b);\n        sEmojisMap.append(0x1f43c, R.drawable.emoji_1f43c);\n        sEmojisMap.append(0x1f43d, R.drawable.emoji_1f43d);\n        sEmojisMap.append(0x1f43e, R.drawable.emoji_1f43e);\n        sEmojisMap.append(0x1f440, R.drawable.emoji_1f440);\n        sEmojisMap.append(0x1f442, R.drawable.emoji_1f442);\n        sEmojisMap.append(0x1f443, R.drawable.emoji_1f443);\n        sEmojisMap.append(0x1f444, R.drawable.emoji_1f444);\n        sEmojisMap.append(0x1f445, R.drawable.emoji_1f445);\n        sEmojisMap.append(0x1f446, R.drawable.emoji_1f446);\n        sEmojisMap.append(0x1f447, R.drawable.emoji_1f447);\n        sEmojisMap.append(0x1f448, R.drawable.emoji_1f448);\n        sEmojisMap.append(0x1f449, R.drawable.emoji_1f449);\n        sEmojisMap.append(0x1f44a, R.drawable.emoji_1f44a);\n        sEmojisMap.append(0x1f44b, R.drawable.emoji_1f44b);\n        sEmojisMap.append(0x1f44c, R.drawable.emoji_1f44c);\n        sEmojisMap.append(0x1f44d, R.drawable.emoji_1f44d);\n        sEmojisMap.append(0x1f44e, R.drawable.emoji_1f44e);\n        sEmojisMap.append(0x1f44f, R.drawable.emoji_1f44f);\n        sEmojisMap.append(0x1f450, R.drawable.emoji_1f450);\n        sEmojisMap.append(0x1f451, R.drawable.emoji_1f451);\n        sEmojisMap.append(0x1f452, R.drawable.emoji_1f452);\n        sEmojisMap.append(0x1f453, R.drawable.emoji_1f453);\n        sEmojisMap.append(0x1f454, R.drawable.emoji_1f454);\n        sEmojisMap.append(0x1f455, R.drawable.emoji_1f455);\n        sEmojisMap.append(0x1f456, R.drawable.emoji_1f456);\n        sEmojisMap.append(0x1f457, R.drawable.emoji_1f457);\n        sEmojisMap.append(0x1f458, R.drawable.emoji_1f458);\n        sEmojisMap.append(0x1f459, R.drawable.emoji_1f459);\n        sEmojisMap.append(0x1f45a, R.drawable.emoji_1f45a);\n        sEmojisMap.append(0x1f45b, R.drawable.emoji_1f45b);\n        sEmojisMap.append(0x1f45c, R.drawable.emoji_1f45c);\n        sEmojisMap.append(0x1f45d, R.drawable.emoji_1f45d);\n        sEmojisMap.append(0x1f45e, R.drawable.emoji_1f45e);\n        sEmojisMap.append(0x1f45f, R.drawable.emoji_1f45f);\n        sEmojisMap.append(0x1f460, R.drawable.emoji_1f460);\n        sEmojisMap.append(0x1f461, R.drawable.emoji_1f461);\n        sEmojisMap.append(0x1f462, R.drawable.emoji_1f462);\n        sEmojisMap.append(0x1f463, R.drawable.emoji_1f463);\n        sEmojisMap.append(0x1f464, R.drawable.emoji_1f464);\n        sEmojisMap.append(0x1f465, R.drawable.emoji_1f465);\n        sEmojisMap.append(0x1f466, R.drawable.emoji_1f466);\n        sEmojisMap.append(0x1f467, R.drawable.emoji_1f467);\n        sEmojisMap.append(0x1f468, R.drawable.emoji_1f468);\n        sEmojisMap.append(0x1f469, R.drawable.emoji_1f469);\n        sEmojisMap.append(0x1f46a, R.drawable.emoji_1f46a);\n        sEmojisMap.append(0x1f46b, R.drawable.emoji_1f46b);\n        sEmojisMap.append(0x1f46c, R.drawable.emoji_1f46c);\n        sEmojisMap.append(0x1f46d, R.drawable.emoji_1f46d);\n        sEmojisMap.append(0x1f46e, R.drawable.emoji_1f46e);\n        sEmojisMap.append(0x1f46f, R.drawable.emoji_1f46f);\n        sEmojisMap.append(0x1f470, R.drawable.emoji_1f470);\n        sEmojisMap.append(0x1f471, R.drawable.emoji_1f471);\n        sEmojisMap.append(0x1f472, R.drawable.emoji_1f472);\n        sEmojisMap.append(0x1f473, R.drawable.emoji_1f473);\n        sEmojisMap.append(0x1f474, R.drawable.emoji_1f474);\n        sEmojisMap.append(0x1f475, R.drawable.emoji_1f475);\n        sEmojisMap.append(0x1f476, R.drawable.emoji_1f476);\n        sEmojisMap.append(0x1f477, R.drawable.emoji_1f477);\n        sEmojisMap.append(0x1f478, R.drawable.emoji_1f478);\n        sEmojisMap.append(0x1f479, R.drawable.emoji_1f479);\n        sEmojisMap.append(0x1f47a, R.drawable.emoji_1f47a);\n        sEmojisMap.append(0x1f47b, R.drawable.emoji_1f47b);\n        sEmojisMap.append(0x1f47c, R.drawable.emoji_1f47c);\n        sEmojisMap.append(0x1f47d, R.drawable.emoji_1f47d);\n        sEmojisMap.append(0x1f47e, R.drawable.emoji_1f47e);\n        sEmojisMap.append(0x1f47f, R.drawable.emoji_1f47f);\n        sEmojisMap.append(0x1f480, R.drawable.emoji_1f480);\n        sEmojisMap.append(0x1f481, R.drawable.emoji_1f481);\n        sEmojisMap.append(0x1f482, R.drawable.emoji_1f482);\n        sEmojisMap.append(0x1f483, R.drawable.emoji_1f483);\n        sEmojisMap.append(0x1f484, R.drawable.emoji_1f484);\n        sEmojisMap.append(0x1f485, R.drawable.emoji_1f485);\n        sEmojisMap.append(0x1f486, R.drawable.emoji_1f486);\n        sEmojisMap.append(0x1f487, R.drawable.emoji_1f487);\n        sEmojisMap.append(0x1f488, R.drawable.emoji_1f488);\n        sEmojisMap.append(0x1f489, R.drawable.emoji_1f489);\n        sEmojisMap.append(0x1f48a, R.drawable.emoji_1f48a);\n        sEmojisMap.append(0x1f48b, R.drawable.emoji_1f48b);\n        sEmojisMap.append(0x1f48c, R.drawable.emoji_1f48c);\n        sEmojisMap.append(0x1f48d, R.drawable.emoji_1f48d);\n        sEmojisMap.append(0x1f48e, R.drawable.emoji_1f48e);\n        sEmojisMap.append(0x1f48f, R.drawable.emoji_1f48f);\n        sEmojisMap.append(0x1f490, R.drawable.emoji_1f490);\n        sEmojisMap.append(0x1f491, R.drawable.emoji_1f491);\n        sEmojisMap.append(0x1f492, R.drawable.emoji_1f492);\n        sEmojisMap.append(0x1f493, R.drawable.emoji_1f493);\n        sEmojisMap.append(0x1f494, R.drawable.emoji_1f494);\n        sEmojisMap.append(0x1f495, R.drawable.emoji_1f495);\n        sEmojisMap.append(0x1f496, R.drawable.emoji_1f496);\n        sEmojisMap.append(0x1f497, R.drawable.emoji_1f497);\n        sEmojisMap.append(0x1f498, R.drawable.emoji_1f498);\n        sEmojisMap.append(0x1f499, R.drawable.emoji_1f499);\n        sEmojisMap.append(0x1f49a, R.drawable.emoji_1f49a);\n        sEmojisMap.append(0x1f49b, R.drawable.emoji_1f49b);\n        sEmojisMap.append(0x1f49c, R.drawable.emoji_1f49c);\n        sEmojisMap.append(0x1f49d, R.drawable.emoji_1f49d);\n        sEmojisMap.append(0x1f49e, R.drawable.emoji_1f49e);\n        sEmojisMap.append(0x1f49f, R.drawable.emoji_1f49f);\n        sEmojisMap.append(0x1f4a0, R.drawable.emoji_1f4a0);\n        sEmojisMap.append(0x1f4a1, R.drawable.emoji_1f4a1);\n        sEmojisMap.append(0x1f4a2, R.drawable.emoji_1f4a2);\n        sEmojisMap.append(0x1f4a3, R.drawable.emoji_1f4a3);\n        sEmojisMap.append(0x1f4a4, R.drawable.emoji_1f4a4);\n        sEmojisMap.append(0x1f4a5, R.drawable.emoji_1f4a5);\n        sEmojisMap.append(0x1f4a6, R.drawable.emoji_1f4a6);\n        sEmojisMap.append(0x1f4a7, R.drawable.emoji_1f4a7);\n        sEmojisMap.append(0x1f4a8, R.drawable.emoji_1f4a8);\n        sEmojisMap.append(0x1f4a9, R.drawable.emoji_1f4a9);\n        sEmojisMap.append(0x1f4aa, R.drawable.emoji_1f4aa);\n        sEmojisMap.append(0x1f4ab, R.drawable.emoji_1f4ab);\n        sEmojisMap.append(0x1f4ac, R.drawable.emoji_1f4ac);\n        sEmojisMap.append(0x1f4ad, R.drawable.emoji_1f4ad);\n        sEmojisMap.append(0x1f4ae, R.drawable.emoji_1f4ae);\n        sEmojisMap.append(0x1f4af, R.drawable.emoji_1f4af);\n        sEmojisMap.append(0x1f4b0, R.drawable.emoji_1f4b0);\n        sEmojisMap.append(0x1f4b1, R.drawable.emoji_1f4b1);\n        sEmojisMap.append(0x1f4b2, R.drawable.emoji_1f4b2);\n        sEmojisMap.append(0x1f4b3, R.drawable.emoji_1f4b3);\n        sEmojisMap.append(0x1f4b4, R.drawable.emoji_1f4b4);\n        sEmojisMap.append(0x1f4b5, R.drawable.emoji_1f4b5);\n        sEmojisMap.append(0x1f4b6, R.drawable.emoji_1f4b6);\n        sEmojisMap.append(0x1f4b7, R.drawable.emoji_1f4b7);\n        sEmojisMap.append(0x1f4b8, R.drawable.emoji_1f4b8);\n        sEmojisMap.append(0x1f4b9, R.drawable.emoji_1f4b9);\n        sEmojisMap.append(0x1f4ba, R.drawable.emoji_1f4ba);\n        sEmojisMap.append(0x1f4bb, R.drawable.emoji_1f4bb);\n        sEmojisMap.append(0x1f4bc, R.drawable.emoji_1f4bc);\n        sEmojisMap.append(0x1f4bd, R.drawable.emoji_1f4bd);\n        sEmojisMap.append(0x1f4be, R.drawable.emoji_1f4be);\n        sEmojisMap.append(0x1f4bf, R.drawable.emoji_1f4bf);\n        sEmojisMap.append(0x1f4c0, R.drawable.emoji_1f4c0);\n        sEmojisMap.append(0x1f4c1, R.drawable.emoji_1f4c1);\n        sEmojisMap.append(0x1f4c2, R.drawable.emoji_1f4c2);\n        sEmojisMap.append(0x1f4c3, R.drawable.emoji_1f4c3);\n        sEmojisMap.append(0x1f4c4, R.drawable.emoji_1f4c4);\n        sEmojisMap.append(0x1f4c5, R.drawable.emoji_1f4c5);\n        sEmojisMap.append(0x1f4c6, R.drawable.emoji_1f4c6);\n        sEmojisMap.append(0x1f4c7, R.drawable.emoji_1f4c7);\n        sEmojisMap.append(0x1f4c8, R.drawable.emoji_1f4c8);\n        sEmojisMap.append(0x1f4c9, R.drawable.emoji_1f4c9);\n        sEmojisMap.append(0x1f4ca, R.drawable.emoji_1f4ca);\n        sEmojisMap.append(0x1f4cb, R.drawable.emoji_1f4cb);\n        sEmojisMap.append(0x1f4cc, R.drawable.emoji_1f4cc);\n        sEmojisMap.append(0x1f4cd, R.drawable.emoji_1f4cd);\n        sEmojisMap.append(0x1f4ce, R.drawable.emoji_1f4ce);\n        sEmojisMap.append(0x1f4cf, R.drawable.emoji_1f4cf);\n        sEmojisMap.append(0x1f4d0, R.drawable.emoji_1f4d0);\n        sEmojisMap.append(0x1f4d1, R.drawable.emoji_1f4d1);\n        sEmojisMap.append(0x1f4d2, R.drawable.emoji_1f4d2);\n        sEmojisMap.append(0x1f4d3, R.drawable.emoji_1f4d3);\n        sEmojisMap.append(0x1f4d4, R.drawable.emoji_1f4d4);\n        sEmojisMap.append(0x1f4d5, R.drawable.emoji_1f4d5);\n        sEmojisMap.append(0x1f4d6, R.drawable.emoji_1f4d6);\n        sEmojisMap.append(0x1f4d7, R.drawable.emoji_1f4d7);\n        sEmojisMap.append(0x1f4d8, R.drawable.emoji_1f4d8);\n        sEmojisMap.append(0x1f4d9, R.drawable.emoji_1f4d9);\n        sEmojisMap.append(0x1f4da, R.drawable.emoji_1f4da);\n        sEmojisMap.append(0x1f4db, R.drawable.emoji_1f4db);\n        sEmojisMap.append(0x1f4dc, R.drawable.emoji_1f4dc);\n        sEmojisMap.append(0x1f4dd, R.drawable.emoji_1f4dd);\n        sEmojisMap.append(0x1f4de, R.drawable.emoji_1f4de);\n        sEmojisMap.append(0x1f4df, R.drawable.emoji_1f4df);\n        sEmojisMap.append(0x1f4e0, R.drawable.emoji_1f4e0);\n        sEmojisMap.append(0x1f4e1, R.drawable.emoji_1f4e1);\n        sEmojisMap.append(0x1f4e2, R.drawable.emoji_1f4e2);\n        sEmojisMap.append(0x1f4e3, R.drawable.emoji_1f4e3);\n        sEmojisMap.append(0x1f4e4, R.drawable.emoji_1f4e4);\n        sEmojisMap.append(0x1f4e5, R.drawable.emoji_1f4e5);\n        sEmojisMap.append(0x1f4e6, R.drawable.emoji_1f4e6);\n        sEmojisMap.append(0x1f4e7, R.drawable.emoji_1f4e7);\n        sEmojisMap.append(0x1f4e8, R.drawable.emoji_1f4e8);\n        sEmojisMap.append(0x1f4e9, R.drawable.emoji_1f4e9);\n        sEmojisMap.append(0x1f4ea, R.drawable.emoji_1f4ea);\n        sEmojisMap.append(0x1f4eb, R.drawable.emoji_1f4eb);\n        sEmojisMap.append(0x1f4ec, R.drawable.emoji_1f4ec);\n        sEmojisMap.append(0x1f4ed, R.drawable.emoji_1f4ed);\n        sEmojisMap.append(0x1f4ee, R.drawable.emoji_1f4ee);\n        sEmojisMap.append(0x1f4ef, R.drawable.emoji_1f4ef);\n        sEmojisMap.append(0x1f4f0, R.drawable.emoji_1f4f0);\n        sEmojisMap.append(0x1f4f1, R.drawable.emoji_1f4f1);\n        sEmojisMap.append(0x1f4f2, R.drawable.emoji_1f4f2);\n        sEmojisMap.append(0x1f4f3, R.drawable.emoji_1f4f3);\n        sEmojisMap.append(0x1f4f4, R.drawable.emoji_1f4f4);\n        sEmojisMap.append(0x1f4f5, R.drawable.emoji_1f4f5);\n        sEmojisMap.append(0x1f4f6, R.drawable.emoji_1f4f6);\n        sEmojisMap.append(0x1f4f7, R.drawable.emoji_1f4f7);\n        sEmojisMap.append(0x1f4f9, R.drawable.emoji_1f4f9);\n        sEmojisMap.append(0x1f4fa, R.drawable.emoji_1f4fa);\n        sEmojisMap.append(0x1f4fb, R.drawable.emoji_1f4fb);\n        sEmojisMap.append(0x1f4fc, R.drawable.emoji_1f4fc);\n        sEmojisMap.append(0x1f500, R.drawable.emoji_1f500);\n        sEmojisMap.append(0x1f501, R.drawable.emoji_1f501);\n        sEmojisMap.append(0x1f502, R.drawable.emoji_1f502);\n        sEmojisMap.append(0x1f503, R.drawable.emoji_1f503);\n        sEmojisMap.append(0x1f504, R.drawable.emoji_1f504);\n        sEmojisMap.append(0x1f505, R.drawable.emoji_1f505);\n        sEmojisMap.append(0x1f506, R.drawable.emoji_1f506);\n        sEmojisMap.append(0x1f507, R.drawable.emoji_1f507);\n        sEmojisMap.append(0x1f508, R.drawable.emoji_1f508);\n        sEmojisMap.append(0x1f509, R.drawable.emoji_1f509);\n        sEmojisMap.append(0x1f50a, R.drawable.emoji_1f50a);\n        sEmojisMap.append(0x1f50b, R.drawable.emoji_1f50b);\n        sEmojisMap.append(0x1f50c, R.drawable.emoji_1f50c);\n        sEmojisMap.append(0x1f50d, R.drawable.emoji_1f50d);\n        sEmojisMap.append(0x1f50e, R.drawable.emoji_1f50e);\n        sEmojisMap.append(0x1f50f, R.drawable.emoji_1f50f);\n        sEmojisMap.append(0x1f510, R.drawable.emoji_1f510);\n        sEmojisMap.append(0x1f511, R.drawable.emoji_1f511);\n        sEmojisMap.append(0x1f512, R.drawable.emoji_1f512);\n        sEmojisMap.append(0x1f513, R.drawable.emoji_1f513);\n        sEmojisMap.append(0x1f514, R.drawable.emoji_1f514);\n        sEmojisMap.append(0x1f515, R.drawable.emoji_1f515);\n        sEmojisMap.append(0x1f516, R.drawable.emoji_1f516);\n        sEmojisMap.append(0x1f517, R.drawable.emoji_1f517);\n        sEmojisMap.append(0x1f518, R.drawable.emoji_1f518);\n        sEmojisMap.append(0x1f519, R.drawable.emoji_1f519);\n        sEmojisMap.append(0x1f51a, R.drawable.emoji_1f51a);\n        sEmojisMap.append(0x1f51b, R.drawable.emoji_1f51b);\n        sEmojisMap.append(0x1f51c, R.drawable.emoji_1f51c);\n        sEmojisMap.append(0x1f51d, R.drawable.emoji_1f51d);\n        sEmojisMap.append(0x1f51e, R.drawable.emoji_1f51e);\n        sEmojisMap.append(0x1f51f, R.drawable.emoji_1f51f);\n        sEmojisMap.append(0x1f520, R.drawable.emoji_1f520);\n        sEmojisMap.append(0x1f521, R.drawable.emoji_1f521);\n        sEmojisMap.append(0x1f522, R.drawable.emoji_1f522);\n        sEmojisMap.append(0x1f523, R.drawable.emoji_1f523);\n        sEmojisMap.append(0x1f524, R.drawable.emoji_1f524);\n        sEmojisMap.append(0x1f525, R.drawable.emoji_1f525);\n        sEmojisMap.append(0x1f526, R.drawable.emoji_1f526);\n        sEmojisMap.append(0x1f527, R.drawable.emoji_1f527);\n        sEmojisMap.append(0x1f528, R.drawable.emoji_1f528);\n        sEmojisMap.append(0x1f529, R.drawable.emoji_1f529);\n        sEmojisMap.append(0x1f52a, R.drawable.emoji_1f52a);\n        sEmojisMap.append(0x1f52b, R.drawable.emoji_1f52b);\n        sEmojisMap.append(0x1f52c, R.drawable.emoji_1f52c);\n        sEmojisMap.append(0x1f52d, R.drawable.emoji_1f52d);\n        sEmojisMap.append(0x1f52e, R.drawable.emoji_1f52e);\n        sEmojisMap.append(0x1f52f, R.drawable.emoji_1f52f);\n        sEmojisMap.append(0x1f530, R.drawable.emoji_1f530);\n        sEmojisMap.append(0x1f531, R.drawable.emoji_1f531);\n        sEmojisMap.append(0x1f532, R.drawable.emoji_1f532);\n        sEmojisMap.append(0x1f533, R.drawable.emoji_1f533);\n        sEmojisMap.append(0x1f534, R.drawable.emoji_1f534);\n        sEmojisMap.append(0x1f535, R.drawable.emoji_1f535);\n        sEmojisMap.append(0x1f536, R.drawable.emoji_1f536);\n        sEmojisMap.append(0x1f537, R.drawable.emoji_1f537);\n        sEmojisMap.append(0x1f538, R.drawable.emoji_1f538);\n        sEmojisMap.append(0x1f539, R.drawable.emoji_1f539);\n        sEmojisMap.append(0x1f53a, R.drawable.emoji_1f53a);\n        sEmojisMap.append(0x1f53b, R.drawable.emoji_1f53b);\n        sEmojisMap.append(0x1f53c, R.drawable.emoji_1f53c);\n        sEmojisMap.append(0x1f53d, R.drawable.emoji_1f53d);\n        sEmojisMap.append(0x1f550, R.drawable.emoji_1f550);\n        sEmojisMap.append(0x1f551, R.drawable.emoji_1f551);\n        sEmojisMap.append(0x1f552, R.drawable.emoji_1f552);\n        sEmojisMap.append(0x1f553, R.drawable.emoji_1f553);\n        sEmojisMap.append(0x1f554, R.drawable.emoji_1f554);\n        sEmojisMap.append(0x1f555, R.drawable.emoji_1f555);\n        sEmojisMap.append(0x1f556, R.drawable.emoji_1f556);\n        sEmojisMap.append(0x1f557, R.drawable.emoji_1f557);\n        sEmojisMap.append(0x1f558, R.drawable.emoji_1f558);\n        sEmojisMap.append(0x1f559, R.drawable.emoji_1f559);\n        sEmojisMap.append(0x1f55a, R.drawable.emoji_1f55a);\n        sEmojisMap.append(0x1f55b, R.drawable.emoji_1f55b);\n        sEmojisMap.append(0x1f55c, R.drawable.emoji_1f55c);\n        sEmojisMap.append(0x1f55d, R.drawable.emoji_1f55d);\n        sEmojisMap.append(0x1f55e, R.drawable.emoji_1f55e);\n        sEmojisMap.append(0x1f55f, R.drawable.emoji_1f55f);\n        sEmojisMap.append(0x1f560, R.drawable.emoji_1f560);\n        sEmojisMap.append(0x1f561, R.drawable.emoji_1f561);\n        sEmojisMap.append(0x1f562, R.drawable.emoji_1f562);\n        sEmojisMap.append(0x1f563, R.drawable.emoji_1f563);\n        sEmojisMap.append(0x1f564, R.drawable.emoji_1f564);\n        sEmojisMap.append(0x1f565, R.drawable.emoji_1f565);\n        sEmojisMap.append(0x1f566, R.drawable.emoji_1f566);\n        sEmojisMap.append(0x1f567, R.drawable.emoji_1f567);\n        sEmojisMap.append(0x1f5fb, R.drawable.emoji_1f5fb);\n        sEmojisMap.append(0x1f5fc, R.drawable.emoji_1f5fc);\n        sEmojisMap.append(0x1f5fd, R.drawable.emoji_1f5fd);\n        sEmojisMap.append(0x1f5fe, R.drawable.emoji_1f5fe);\n        sEmojisMap.append(0x1f5ff, R.drawable.emoji_1f5ff);\n        sEmojisMap.append(0x1f600, R.drawable.emoji_1f600);\n        sEmojisMap.append(0x1f601, R.drawable.emoji_1f601);\n        sEmojisMap.append(0x1f602, R.drawable.emoji_1f602);\n        sEmojisMap.append(0x1f603, R.drawable.emoji_1f603);\n        sEmojisMap.append(0x1f604, R.drawable.emoji_1f604);\n        sEmojisMap.append(0x1f605, R.drawable.emoji_1f605);\n        sEmojisMap.append(0x1f606, R.drawable.emoji_1f606);\n        sEmojisMap.append(0x1f607, R.drawable.emoji_1f607);\n        sEmojisMap.append(0x1f608, R.drawable.emoji_1f608);\n        sEmojisMap.append(0x1f609, R.drawable.emoji_1f609);\n        sEmojisMap.append(0x1f60a, R.drawable.emoji_1f60a);\n        sEmojisMap.append(0x1f60b, R.drawable.emoji_1f60b);\n        sEmojisMap.append(0x1f60c, R.drawable.emoji_1f60c);\n        sEmojisMap.append(0x1f60d, R.drawable.emoji_1f60d);\n        sEmojisMap.append(0x1f60e, R.drawable.emoji_1f60e);\n        sEmojisMap.append(0x1f60f, R.drawable.emoji_1f60f);\n        sEmojisMap.append(0x1f610, R.drawable.emoji_1f610);\n        sEmojisMap.append(0x1f611, R.drawable.emoji_1f611);\n        sEmojisMap.append(0x1f612, R.drawable.emoji_1f612);\n        sEmojisMap.append(0x1f613, R.drawable.emoji_1f613);\n        sEmojisMap.append(0x1f614, R.drawable.emoji_1f614);\n        sEmojisMap.append(0x1f615, R.drawable.emoji_1f615);\n        sEmojisMap.append(0x1f616, R.drawable.emoji_1f616);\n        sEmojisMap.append(0x1f617, R.drawable.emoji_1f617);\n        sEmojisMap.append(0x1f618, R.drawable.emoji_1f618);\n        sEmojisMap.append(0x1f619, R.drawable.emoji_1f619);\n        sEmojisMap.append(0x1f61a, R.drawable.emoji_1f61a);\n        sEmojisMap.append(0x1f61b, R.drawable.emoji_1f61b);\n        sEmojisMap.append(0x1f61c, R.drawable.emoji_1f61c);\n        sEmojisMap.append(0x1f61d, R.drawable.emoji_1f61d);\n        sEmojisMap.append(0x1f61e, R.drawable.emoji_1f61e);\n        sEmojisMap.append(0x1f61f, R.drawable.emoji_1f61f);\n        sEmojisMap.append(0x1f620, R.drawable.emoji_1f620);\n        sEmojisMap.append(0x1f621, R.drawable.emoji_1f621);\n        sEmojisMap.append(0x1f622, R.drawable.emoji_1f622);\n        sEmojisMap.append(0x1f623, R.drawable.emoji_1f623);\n        sEmojisMap.append(0x1f624, R.drawable.emoji_1f624);\n        sEmojisMap.append(0x1f625, R.drawable.emoji_1f625);\n        sEmojisMap.append(0x1f626, R.drawable.emoji_1f626);\n        sEmojisMap.append(0x1f627, R.drawable.emoji_1f627);\n        sEmojisMap.append(0x1f628, R.drawable.emoji_1f628);\n        sEmojisMap.append(0x1f629, R.drawable.emoji_1f629);\n        sEmojisMap.append(0x1f62a, R.drawable.emoji_1f62a);\n        sEmojisMap.append(0x1f62b, R.drawable.emoji_1f62b);\n        sEmojisMap.append(0x1f62c, R.drawable.emoji_1f62c);\n        sEmojisMap.append(0x1f62d, R.drawable.emoji_1f62d);\n        sEmojisMap.append(0x1f62e, R.drawable.emoji_1f62e);\n        sEmojisMap.append(0x1f62f, R.drawable.emoji_1f62f);\n        sEmojisMap.append(0x1f630, R.drawable.emoji_1f630);\n        sEmojisMap.append(0x1f631, R.drawable.emoji_1f631);\n        sEmojisMap.append(0x1f632, R.drawable.emoji_1f632);\n        sEmojisMap.append(0x1f633, R.drawable.emoji_1f633);\n        sEmojisMap.append(0x1f634, R.drawable.emoji_1f634);\n        sEmojisMap.append(0x1f635, R.drawable.emoji_1f635);\n        sEmojisMap.append(0x1f636, R.drawable.emoji_1f636);\n        sEmojisMap.append(0x1f637, R.drawable.emoji_1f637);\n        sEmojisMap.append(0x1f638, R.drawable.emoji_1f638);\n        sEmojisMap.append(0x1f639, R.drawable.emoji_1f639);\n        sEmojisMap.append(0x1f63a, R.drawable.emoji_1f63a);\n        sEmojisMap.append(0x1f63b, R.drawable.emoji_1f63b);\n        sEmojisMap.append(0x1f63c, R.drawable.emoji_1f63c);\n        sEmojisMap.append(0x1f63d, R.drawable.emoji_1f63d);\n        sEmojisMap.append(0x1f63e, R.drawable.emoji_1f63e);\n        sEmojisMap.append(0x1f63f, R.drawable.emoji_1f63f);\n        sEmojisMap.append(0x1f640, R.drawable.emoji_1f640);\n        sEmojisMap.append(0x1f645, R.drawable.emoji_1f645);\n        sEmojisMap.append(0x1f646, R.drawable.emoji_1f646);\n        sEmojisMap.append(0x1f647, R.drawable.emoji_1f647);\n        sEmojisMap.append(0x1f648, R.drawable.emoji_1f648);\n        sEmojisMap.append(0x1f649, R.drawable.emoji_1f649);\n        sEmojisMap.append(0x1f64a, R.drawable.emoji_1f64a);\n        sEmojisMap.append(0x1f64b, R.drawable.emoji_1f64b);\n        sEmojisMap.append(0x1f64c, R.drawable.emoji_1f64c);\n        sEmojisMap.append(0x1f64d, R.drawable.emoji_1f64d);\n        sEmojisMap.append(0x1f64e, R.drawable.emoji_1f64e);\n        sEmojisMap.append(0x1f64f, R.drawable.emoji_1f64f);\n        sEmojisMap.append(0x1f680, R.drawable.emoji_1f680);\n        sEmojisMap.append(0x1f681, R.drawable.emoji_1f681);\n        sEmojisMap.append(0x1f682, R.drawable.emoji_1f682);\n        sEmojisMap.append(0x1f683, R.drawable.emoji_1f683);\n        sEmojisMap.append(0x1f684, R.drawable.emoji_1f684);\n        sEmojisMap.append(0x1f685, R.drawable.emoji_1f685);\n        sEmojisMap.append(0x1f686, R.drawable.emoji_1f686);\n        sEmojisMap.append(0x1f687, R.drawable.emoji_1f687);\n        sEmojisMap.append(0x1f688, R.drawable.emoji_1f688);\n        sEmojisMap.append(0x1f689, R.drawable.emoji_1f689);\n        sEmojisMap.append(0x1f68a, R.drawable.emoji_1f68a);\n        sEmojisMap.append(0x1f68b, R.drawable.emoji_1f68b);\n        sEmojisMap.append(0x1f68c, R.drawable.emoji_1f68c);\n        sEmojisMap.append(0x1f68d, R.drawable.emoji_1f68d);\n        sEmojisMap.append(0x1f68e, R.drawable.emoji_1f68e);\n        sEmojisMap.append(0x1f68f, R.drawable.emoji_1f68f);\n        sEmojisMap.append(0x1f690, R.drawable.emoji_1f690);\n        sEmojisMap.append(0x1f691, R.drawable.emoji_1f691);\n        sEmojisMap.append(0x1f692, R.drawable.emoji_1f692);\n        sEmojisMap.append(0x1f693, R.drawable.emoji_1f693);\n        sEmojisMap.append(0x1f694, R.drawable.emoji_1f694);\n        sEmojisMap.append(0x1f695, R.drawable.emoji_1f695);\n        sEmojisMap.append(0x1f696, R.drawable.emoji_1f696);\n        sEmojisMap.append(0x1f697, R.drawable.emoji_1f697);\n        sEmojisMap.append(0x1f698, R.drawable.emoji_1f698);\n        sEmojisMap.append(0x1f699, R.drawable.emoji_1f699);\n        sEmojisMap.append(0x1f69a, R.drawable.emoji_1f69a);\n        sEmojisMap.append(0x1f69b, R.drawable.emoji_1f69b);\n        sEmojisMap.append(0x1f69c, R.drawable.emoji_1f69c);\n        sEmojisMap.append(0x1f69d, R.drawable.emoji_1f69d);\n        sEmojisMap.append(0x1f69e, R.drawable.emoji_1f69e);\n        sEmojisMap.append(0x1f69f, R.drawable.emoji_1f69f);\n        sEmojisMap.append(0x1f6a0, R.drawable.emoji_1f6a0);\n        sEmojisMap.append(0x1f6a1, R.drawable.emoji_1f6a1);\n        sEmojisMap.append(0x1f6a2, R.drawable.emoji_1f6a2);\n        sEmojisMap.append(0x1f6a3, R.drawable.emoji_1f6a3);\n        sEmojisMap.append(0x1f6a4, R.drawable.emoji_1f6a4);\n        sEmojisMap.append(0x1f6a5, R.drawable.emoji_1f6a5);\n        sEmojisMap.append(0x1f6a6, R.drawable.emoji_1f6a6);\n        sEmojisMap.append(0x1f6a7, R.drawable.emoji_1f6a7);\n        sEmojisMap.append(0x1f6a8, R.drawable.emoji_1f6a8);\n        sEmojisMap.append(0x1f6a9, R.drawable.emoji_1f6a9);\n        sEmojisMap.append(0x1f6aa, R.drawable.emoji_1f6aa);\n        sEmojisMap.append(0x1f6ab, R.drawable.emoji_1f6ab);\n        sEmojisMap.append(0x1f6ac, R.drawable.emoji_1f6ac);\n        sEmojisMap.append(0x1f6ad, R.drawable.emoji_1f6ad);\n        sEmojisMap.append(0x1f6ae, R.drawable.emoji_1f6ae);\n        sEmojisMap.append(0x1f6af, R.drawable.emoji_1f6af);\n        sEmojisMap.append(0x1f6b0, R.drawable.emoji_1f6b0);\n        sEmojisMap.append(0x1f6b1, R.drawable.emoji_1f6b1);\n        sEmojisMap.append(0x1f6b2, R.drawable.emoji_1f6b2);\n        sEmojisMap.append(0x1f6b3, R.drawable.emoji_1f6b3);\n        sEmojisMap.append(0x1f6b4, R.drawable.emoji_1f6b4);\n        sEmojisMap.append(0x1f6b5, R.drawable.emoji_1f6b5);\n        sEmojisMap.append(0x1f6b6, R.drawable.emoji_1f6b6);\n        sEmojisMap.append(0x1f6b7, R.drawable.emoji_1f6b7);\n        sEmojisMap.append(0x1f6b8, R.drawable.emoji_1f6b8);\n        sEmojisMap.append(0x1f6b9, R.drawable.emoji_1f6b9);\n        sEmojisMap.append(0x1f6ba, R.drawable.emoji_1f6ba);\n        sEmojisMap.append(0x1f6bb, R.drawable.emoji_1f6bb);\n        sEmojisMap.append(0x1f6bc, R.drawable.emoji_1f6bc);\n        sEmojisMap.append(0x1f6bd, R.drawable.emoji_1f6bd);\n        sEmojisMap.append(0x1f6be, R.drawable.emoji_1f6be);\n        sEmojisMap.append(0x1f6bf, R.drawable.emoji_1f6bf);\n        sEmojisMap.append(0x1f6c0, R.drawable.emoji_1f6c0);\n        sEmojisMap.append(0x1f6c1, R.drawable.emoji_1f6c1);\n        sEmojisMap.append(0x1f6c2, R.drawable.emoji_1f6c2);\n        sEmojisMap.append(0x1f6c3, R.drawable.emoji_1f6c3);\n        sEmojisMap.append(0x1f6c4, R.drawable.emoji_1f6c4);\n        sEmojisMap.append(0x1f6c5, R.drawable.emoji_1f6c5);\n\n\n        sSoftbanksMap.append(0xe001, R.drawable.emoji_1f466);\n        sSoftbanksMap.append(0xe002, R.drawable.emoji_1f467);\n        sSoftbanksMap.append(0xe003, R.drawable.emoji_1f48b);\n        sSoftbanksMap.append(0xe004, R.drawable.emoji_1f468);\n        sSoftbanksMap.append(0xe005, R.drawable.emoji_1f469);\n        sSoftbanksMap.append(0xe006, R.drawable.emoji_1f455);\n        sSoftbanksMap.append(0xe007, R.drawable.emoji_1f45e);\n        sSoftbanksMap.append(0xe008, R.drawable.emoji_1f4f7);\n        sSoftbanksMap.append(0xe009, R.drawable.emoji_1f4de);\n        sSoftbanksMap.append(0xe00a, R.drawable.emoji_1f4f1);\n        sSoftbanksMap.append(0xe00b, R.drawable.emoji_1f4e0);\n        sSoftbanksMap.append(0xe00c, R.drawable.emoji_1f4bb);\n        sSoftbanksMap.append(0xe00d, R.drawable.emoji_1f44a);\n        sSoftbanksMap.append(0xe00e, R.drawable.emoji_1f44d);\n        sSoftbanksMap.append(0xe00f, R.drawable.emoji_261d);\n        sSoftbanksMap.append(0xe010, R.drawable.emoji_270a);\n        sSoftbanksMap.append(0xe011, R.drawable.emoji_270c);\n        sSoftbanksMap.append(0xe012, R.drawable.emoji_1f64b);\n        sSoftbanksMap.append(0xe013, R.drawable.emoji_1f3bf);\n        sSoftbanksMap.append(0xe014, R.drawable.emoji_26f3);\n        sSoftbanksMap.append(0xe015, R.drawable.emoji_1f3be);\n        sSoftbanksMap.append(0xe016, R.drawable.emoji_26be);\n        sSoftbanksMap.append(0xe017, R.drawable.emoji_1f3c4);\n        sSoftbanksMap.append(0xe018, R.drawable.emoji_26bd);\n        sSoftbanksMap.append(0xe019, R.drawable.emoji_1f3a3);\n        sSoftbanksMap.append(0xe01a, R.drawable.emoji_1f434);\n        sSoftbanksMap.append(0xe01b, R.drawable.emoji_1f697);\n        sSoftbanksMap.append(0xe01c, R.drawable.emoji_26f5);\n        sSoftbanksMap.append(0xe01d, R.drawable.emoji_2708);\n        sSoftbanksMap.append(0xe01e, R.drawable.emoji_1f683);\n        sSoftbanksMap.append(0xe01f, R.drawable.emoji_1f685);\n        sSoftbanksMap.append(0xe020, R.drawable.emoji_2753);\n        sSoftbanksMap.append(0xe021, R.drawable.emoji_2757);\n        sSoftbanksMap.append(0xe022, R.drawable.emoji_2764);\n        sSoftbanksMap.append(0xe023, R.drawable.emoji_1f494);\n        sSoftbanksMap.append(0xe024, R.drawable.emoji_1f550);\n        sSoftbanksMap.append(0xe025, R.drawable.emoji_1f551);\n        sSoftbanksMap.append(0xe026, R.drawable.emoji_1f552);\n        sSoftbanksMap.append(0xe027, R.drawable.emoji_1f553);\n        sSoftbanksMap.append(0xe028, R.drawable.emoji_1f554);\n        sSoftbanksMap.append(0xe029, R.drawable.emoji_1f555);\n        sSoftbanksMap.append(0xe02a, R.drawable.emoji_1f556);\n        sSoftbanksMap.append(0xe02b, R.drawable.emoji_1f557);\n        sSoftbanksMap.append(0xe02c, R.drawable.emoji_1f558);\n        sSoftbanksMap.append(0xe02d, R.drawable.emoji_1f559);\n        sSoftbanksMap.append(0xe02e, R.drawable.emoji_1f55a);\n        sSoftbanksMap.append(0xe02f, R.drawable.emoji_1f55b);\n        sSoftbanksMap.append(0xe030, R.drawable.emoji_1f338);\n        sSoftbanksMap.append(0xe031, R.drawable.emoji_1f531);\n        sSoftbanksMap.append(0xe032, R.drawable.emoji_1f339);\n        sSoftbanksMap.append(0xe033, R.drawable.emoji_1f384);\n        sSoftbanksMap.append(0xe034, R.drawable.emoji_1f48d);\n        sSoftbanksMap.append(0xe035, R.drawable.emoji_1f48e);\n        sSoftbanksMap.append(0xe036, R.drawable.emoji_1f3e0);\n        sSoftbanksMap.append(0xe037, R.drawable.emoji_26ea);\n        sSoftbanksMap.append(0xe038, R.drawable.emoji_1f3e2);\n        sSoftbanksMap.append(0xe039, R.drawable.emoji_1f689);\n        sSoftbanksMap.append(0xe03a, R.drawable.emoji_26fd);\n        sSoftbanksMap.append(0xe03b, R.drawable.emoji_1f5fb);\n        sSoftbanksMap.append(0xe03c, R.drawable.emoji_1f3a4);\n        sSoftbanksMap.append(0xe03d, R.drawable.emoji_1f3a5);\n        sSoftbanksMap.append(0xe03e, R.drawable.emoji_1f3b5);\n        sSoftbanksMap.append(0xe03f, R.drawable.emoji_1f511);\n        sSoftbanksMap.append(0xe040, R.drawable.emoji_1f3b7);\n        sSoftbanksMap.append(0xe041, R.drawable.emoji_1f3b8);\n        sSoftbanksMap.append(0xe042, R.drawable.emoji_1f3ba);\n        sSoftbanksMap.append(0xe043, R.drawable.emoji_1f374);\n        sSoftbanksMap.append(0xe044, R.drawable.emoji_1f377);\n        sSoftbanksMap.append(0xe045, R.drawable.emoji_2615);\n        sSoftbanksMap.append(0xe046, R.drawable.emoji_1f370);\n        sSoftbanksMap.append(0xe047, R.drawable.emoji_1f37a);\n        sSoftbanksMap.append(0xe048, R.drawable.emoji_26c4);\n        sSoftbanksMap.append(0xe049, R.drawable.emoji_2601);\n        sSoftbanksMap.append(0xe04a, R.drawable.emoji_2600);\n        sSoftbanksMap.append(0xe04b, R.drawable.emoji_2614);\n        sSoftbanksMap.append(0xe04c, R.drawable.emoji_1f313);\n        sSoftbanksMap.append(0xe04d, R.drawable.emoji_1f304);\n        sSoftbanksMap.append(0xe04e, R.drawable.emoji_1f47c);\n        sSoftbanksMap.append(0xe04f, R.drawable.emoji_1f431);\n        sSoftbanksMap.append(0xe050, R.drawable.emoji_1f42f);\n        sSoftbanksMap.append(0xe051, R.drawable.emoji_1f43b);\n        sSoftbanksMap.append(0xe052, R.drawable.emoji_1f429);\n        sSoftbanksMap.append(0xe053, R.drawable.emoji_1f42d);\n        sSoftbanksMap.append(0xe054, R.drawable.emoji_1f433);\n        sSoftbanksMap.append(0xe055, R.drawable.emoji_1f427);\n        sSoftbanksMap.append(0xe056, R.drawable.emoji_1f60a);\n        sSoftbanksMap.append(0xe057, R.drawable.emoji_1f603);\n        sSoftbanksMap.append(0xe058, R.drawable.emoji_1f61e);\n        sSoftbanksMap.append(0xe059, R.drawable.emoji_1f620);\n        sSoftbanksMap.append(0xe05a, R.drawable.emoji_1f4a9);\n        sSoftbanksMap.append(0xe101, R.drawable.emoji_1f4ea);\n        sSoftbanksMap.append(0xe102, R.drawable.emoji_1f4ee);\n        sSoftbanksMap.append(0xe103, R.drawable.emoji_1f4e7);\n        sSoftbanksMap.append(0xe104, R.drawable.emoji_1f4f2);\n        sSoftbanksMap.append(0xe105, R.drawable.emoji_1f61c);\n        sSoftbanksMap.append(0xe106, R.drawable.emoji_1f60d);\n        sSoftbanksMap.append(0xe107, R.drawable.emoji_1f631);\n        sSoftbanksMap.append(0xe108, R.drawable.emoji_1f613);\n        sSoftbanksMap.append(0xe109, R.drawable.emoji_1f435);\n        sSoftbanksMap.append(0xe10a, R.drawable.emoji_1f419);\n        sSoftbanksMap.append(0xe10b, R.drawable.emoji_1f437);\n        sSoftbanksMap.append(0xe10c, R.drawable.emoji_1f47d);\n        sSoftbanksMap.append(0xe10d, R.drawable.emoji_1f680);\n        sSoftbanksMap.append(0xe10e, R.drawable.emoji_1f451);\n        sSoftbanksMap.append(0xe10f, R.drawable.emoji_1f4a1);\n        sSoftbanksMap.append(0xe110, R.drawable.emoji_1f331);\n        sSoftbanksMap.append(0xe111, R.drawable.emoji_1f48f);\n        sSoftbanksMap.append(0xe112, R.drawable.emoji_1f381);\n        sSoftbanksMap.append(0xe113, R.drawable.emoji_1f52b);\n        sSoftbanksMap.append(0xe114, R.drawable.emoji_1f50d);\n        sSoftbanksMap.append(0xe115, R.drawable.emoji_1f3c3);\n        sSoftbanksMap.append(0xe116, R.drawable.emoji_1f528);\n        sSoftbanksMap.append(0xe117, R.drawable.emoji_1f386);\n        sSoftbanksMap.append(0xe118, R.drawable.emoji_1f341);\n        sSoftbanksMap.append(0xe119, R.drawable.emoji_1f342);\n        sSoftbanksMap.append(0xe11a, R.drawable.emoji_1f47f);\n        sSoftbanksMap.append(0xe11b, R.drawable.emoji_1f47b);\n        sSoftbanksMap.append(0xe11c, R.drawable.emoji_1f480);\n        sSoftbanksMap.append(0xe11d, R.drawable.emoji_1f525);\n        sSoftbanksMap.append(0xe11e, R.drawable.emoji_1f4bc);\n        sSoftbanksMap.append(0xe11f, R.drawable.emoji_1f4ba);\n        sSoftbanksMap.append(0xe120, R.drawable.emoji_1f354);\n        sSoftbanksMap.append(0xe121, R.drawable.emoji_26f2);\n        sSoftbanksMap.append(0xe122, R.drawable.emoji_26fa);\n        sSoftbanksMap.append(0xe123, R.drawable.emoji_2668);\n        sSoftbanksMap.append(0xe124, R.drawable.emoji_1f3a1);\n        sSoftbanksMap.append(0xe125, R.drawable.emoji_1f3ab);\n        sSoftbanksMap.append(0xe126, R.drawable.emoji_1f4bf);\n        sSoftbanksMap.append(0xe127, R.drawable.emoji_1f4c0);\n        sSoftbanksMap.append(0xe128, R.drawable.emoji_1f4fb);\n        sSoftbanksMap.append(0xe129, R.drawable.emoji_1f4fc);\n        sSoftbanksMap.append(0xe12a, R.drawable.emoji_1f4fa);\n        sSoftbanksMap.append(0xe12b, R.drawable.emoji_1f47e);\n        sSoftbanksMap.append(0xe12c, R.drawable.emoji_303d);\n        sSoftbanksMap.append(0xe12d, R.drawable.emoji_1f004);\n        sSoftbanksMap.append(0xe12e, R.drawable.emoji_1f19a);\n        sSoftbanksMap.append(0xe12f, R.drawable.emoji_1f4b0);\n        sSoftbanksMap.append(0xe130, R.drawable.emoji_1f3af);\n        sSoftbanksMap.append(0xe131, R.drawable.emoji_1f3c6);\n        sSoftbanksMap.append(0xe132, R.drawable.emoji_1f3c1);\n        sSoftbanksMap.append(0xe133, R.drawable.emoji_1f3b0);\n        sSoftbanksMap.append(0xe134, R.drawable.emoji_1f40e);\n        sSoftbanksMap.append(0xe135, R.drawable.emoji_1f6a4);\n        sSoftbanksMap.append(0xe136, R.drawable.emoji_1f6b2);\n        sSoftbanksMap.append(0xe137, R.drawable.emoji_1f6a7);\n        sSoftbanksMap.append(0xe138, R.drawable.emoji_1f6b9);\n        sSoftbanksMap.append(0xe139, R.drawable.emoji_1f6ba);\n        sSoftbanksMap.append(0xe13a, R.drawable.emoji_1f6bc);\n        sSoftbanksMap.append(0xe13b, R.drawable.emoji_1f489);\n        sSoftbanksMap.append(0xe13c, R.drawable.emoji_1f4a4);\n        sSoftbanksMap.append(0xe13d, R.drawable.emoji_26a1);\n        sSoftbanksMap.append(0xe13e, R.drawable.emoji_1f460);\n        sSoftbanksMap.append(0xe13f, R.drawable.emoji_1f6c0);\n        sSoftbanksMap.append(0xe140, R.drawable.emoji_1f6bd);\n        sSoftbanksMap.append(0xe141, R.drawable.emoji_1f50a);\n        sSoftbanksMap.append(0xe142, R.drawable.emoji_1f4e2);\n        sSoftbanksMap.append(0xe143, R.drawable.emoji_1f38c);\n        sSoftbanksMap.append(0xe144, R.drawable.emoji_1f50f);\n        sSoftbanksMap.append(0xe145, R.drawable.emoji_1f513);\n        sSoftbanksMap.append(0xe146, R.drawable.emoji_1f306);\n        sSoftbanksMap.append(0xe147, R.drawable.emoji_1f373);\n        sSoftbanksMap.append(0xe148, R.drawable.emoji_1f4c7);\n        sSoftbanksMap.append(0xe149, R.drawable.emoji_1f4b1);\n        sSoftbanksMap.append(0xe14a, R.drawable.emoji_1f4b9);\n        sSoftbanksMap.append(0xe14b, R.drawable.emoji_1f4e1);\n        sSoftbanksMap.append(0xe14c, R.drawable.emoji_1f4aa);\n        sSoftbanksMap.append(0xe14d, R.drawable.emoji_1f3e6);\n        sSoftbanksMap.append(0xe14e, R.drawable.emoji_1f6a5);\n        sSoftbanksMap.append(0xe14f, R.drawable.emoji_1f17f);\n        sSoftbanksMap.append(0xe150, R.drawable.emoji_1f68f);\n        sSoftbanksMap.append(0xe151, R.drawable.emoji_1f6bb);\n        sSoftbanksMap.append(0xe152, R.drawable.emoji_1f46e);\n        sSoftbanksMap.append(0xe153, R.drawable.emoji_1f3e3);\n        sSoftbanksMap.append(0xe154, R.drawable.emoji_1f3e7);\n        sSoftbanksMap.append(0xe155, R.drawable.emoji_1f3e5);\n        sSoftbanksMap.append(0xe156, R.drawable.emoji_1f3ea);\n        sSoftbanksMap.append(0xe157, R.drawable.emoji_1f3eb);\n        sSoftbanksMap.append(0xe158, R.drawable.emoji_1f3e8);\n        sSoftbanksMap.append(0xe159, R.drawable.emoji_1f68c);\n        sSoftbanksMap.append(0xe15a, R.drawable.emoji_1f695);\n        sSoftbanksMap.append(0xe201, R.drawable.emoji_1f6b6);\n        sSoftbanksMap.append(0xe202, R.drawable.emoji_1f6a2);\n        sSoftbanksMap.append(0xe203, R.drawable.emoji_1f201);\n        sSoftbanksMap.append(0xe204, R.drawable.emoji_1f49f);\n        sSoftbanksMap.append(0xe205, R.drawable.emoji_2734);\n        sSoftbanksMap.append(0xe206, R.drawable.emoji_2733);\n        sSoftbanksMap.append(0xe207, R.drawable.emoji_1f51e);\n        sSoftbanksMap.append(0xe208, R.drawable.emoji_1f6ad);\n        sSoftbanksMap.append(0xe209, R.drawable.emoji_1f530);\n        sSoftbanksMap.append(0xe20a, R.drawable.emoji_267f);\n        sSoftbanksMap.append(0xe20b, R.drawable.emoji_1f4f6);\n        sSoftbanksMap.append(0xe20c, R.drawable.emoji_2665);\n        sSoftbanksMap.append(0xe20d, R.drawable.emoji_2666);\n        sSoftbanksMap.append(0xe20e, R.drawable.emoji_2660);\n        sSoftbanksMap.append(0xe20f, R.drawable.emoji_2663);\n        sSoftbanksMap.append(0xe210, R.drawable.emoji_0023);\n        sSoftbanksMap.append(0xe211, R.drawable.emoji_27bf);\n        sSoftbanksMap.append(0xe212, R.drawable.emoji_1f195);\n        sSoftbanksMap.append(0xe213, R.drawable.emoji_1f199);\n        sSoftbanksMap.append(0xe214, R.drawable.emoji_1f192);\n        sSoftbanksMap.append(0xe215, R.drawable.emoji_1f236);\n        sSoftbanksMap.append(0xe216, R.drawable.emoji_1f21a);\n        sSoftbanksMap.append(0xe217, R.drawable.emoji_1f237);\n        sSoftbanksMap.append(0xe218, R.drawable.emoji_1f238);\n        sSoftbanksMap.append(0xe219, R.drawable.emoji_1f534);\n        sSoftbanksMap.append(0xe21a, R.drawable.emoji_1f532);\n        sSoftbanksMap.append(0xe21b, R.drawable.emoji_1f533);\n        sSoftbanksMap.append(0xe21c, R.drawable.emoji_0031);\n        sSoftbanksMap.append(0xe21d, R.drawable.emoji_0032);\n        sSoftbanksMap.append(0xe21e, R.drawable.emoji_0033);\n        sSoftbanksMap.append(0xe21f, R.drawable.emoji_0034);\n        sSoftbanksMap.append(0xe220, R.drawable.emoji_0035);\n        sSoftbanksMap.append(0xe221, R.drawable.emoji_0036);\n        sSoftbanksMap.append(0xe222, R.drawable.emoji_0037);\n        sSoftbanksMap.append(0xe223, R.drawable.emoji_0038);\n        sSoftbanksMap.append(0xe224, R.drawable.emoji_0039);\n        sSoftbanksMap.append(0xe225, R.drawable.emoji_0030);\n        sSoftbanksMap.append(0xe226, R.drawable.emoji_1f250);\n        sSoftbanksMap.append(0xe227, R.drawable.emoji_1f239);\n        sSoftbanksMap.append(0xe228, R.drawable.emoji_1f202);\n        sSoftbanksMap.append(0xe229, R.drawable.emoji_1f194);\n        sSoftbanksMap.append(0xe22a, R.drawable.emoji_1f235);\n        sSoftbanksMap.append(0xe22b, R.drawable.emoji_1f233);\n        sSoftbanksMap.append(0xe22c, R.drawable.emoji_1f22f);\n        sSoftbanksMap.append(0xe22d, R.drawable.emoji_1f23a);\n        sSoftbanksMap.append(0xe22e, R.drawable.emoji_1f446);\n        sSoftbanksMap.append(0xe22f, R.drawable.emoji_1f447);\n        sSoftbanksMap.append(0xe230, R.drawable.emoji_1f448);\n        sSoftbanksMap.append(0xe231, R.drawable.emoji_1f449);\n        sSoftbanksMap.append(0xe232, R.drawable.emoji_2b06);\n        sSoftbanksMap.append(0xe233, R.drawable.emoji_2b07);\n        sSoftbanksMap.append(0xe234, R.drawable.emoji_27a1);\n        sSoftbanksMap.append(0xe235, R.drawable.emoji_1f519);\n        sSoftbanksMap.append(0xe236, R.drawable.emoji_2197);\n        sSoftbanksMap.append(0xe237, R.drawable.emoji_2196);\n        sSoftbanksMap.append(0xe238, R.drawable.emoji_2198);\n        sSoftbanksMap.append(0xe239, R.drawable.emoji_2199);\n        sSoftbanksMap.append(0xe23a, R.drawable.emoji_25b6);\n        sSoftbanksMap.append(0xe23b, R.drawable.emoji_25c0);\n        sSoftbanksMap.append(0xe23c, R.drawable.emoji_23e9);\n        sSoftbanksMap.append(0xe23d, R.drawable.emoji_23ea);\n        sSoftbanksMap.append(0xe23e, R.drawable.emoji_1f52e);\n        sSoftbanksMap.append(0xe23f, R.drawable.emoji_2648);\n        sSoftbanksMap.append(0xe240, R.drawable.emoji_2649);\n        sSoftbanksMap.append(0xe241, R.drawable.emoji_264a);\n        sSoftbanksMap.append(0xe242, R.drawable.emoji_264b);\n        sSoftbanksMap.append(0xe243, R.drawable.emoji_264c);\n        sSoftbanksMap.append(0xe244, R.drawable.emoji_264d);\n        sSoftbanksMap.append(0xe245, R.drawable.emoji_264e);\n        sSoftbanksMap.append(0xe246, R.drawable.emoji_264f);\n        sSoftbanksMap.append(0xe247, R.drawable.emoji_2650);\n        sSoftbanksMap.append(0xe248, R.drawable.emoji_2651);\n        sSoftbanksMap.append(0xe249, R.drawable.emoji_2652);\n        sSoftbanksMap.append(0xe24a, R.drawable.emoji_2653);\n        sSoftbanksMap.append(0xe24b, R.drawable.emoji_26ce);\n        sSoftbanksMap.append(0xe24c, R.drawable.emoji_1f51d);\n        sSoftbanksMap.append(0xe24d, R.drawable.emoji_1f197);\n        sSoftbanksMap.append(0xe24e, R.drawable.emoji_00a9);\n        sSoftbanksMap.append(0xe24f, R.drawable.emoji_00ae);\n        sSoftbanksMap.append(0xe250, R.drawable.emoji_1f4f3);\n        sSoftbanksMap.append(0xe251, R.drawable.emoji_1f4f4);\n        sSoftbanksMap.append(0xe252, R.drawable.emoji_26a0);\n        sSoftbanksMap.append(0xe253, R.drawable.emoji_1f481);\n        sSoftbanksMap.append(0xe301, R.drawable.emoji_1f4c3);\n        sSoftbanksMap.append(0xe302, R.drawable.emoji_1f454);\n        sSoftbanksMap.append(0xe303, R.drawable.emoji_1f33a);\n        sSoftbanksMap.append(0xe304, R.drawable.emoji_1f337);\n        sSoftbanksMap.append(0xe305, R.drawable.emoji_1f33b);\n        sSoftbanksMap.append(0xe306, R.drawable.emoji_1f490);\n        sSoftbanksMap.append(0xe307, R.drawable.emoji_1f334);\n        sSoftbanksMap.append(0xe308, R.drawable.emoji_1f335);\n        sSoftbanksMap.append(0xe309, R.drawable.emoji_1f6be);\n        sSoftbanksMap.append(0xe30a, R.drawable.emoji_1f3a7);\n        sSoftbanksMap.append(0xe30b, R.drawable.emoji_1f376);\n        sSoftbanksMap.append(0xe30c, R.drawable.emoji_1f37b);\n        sSoftbanksMap.append(0xe30d, R.drawable.emoji_3297);\n        sSoftbanksMap.append(0xe30e, R.drawable.emoji_1f6ac);\n        sSoftbanksMap.append(0xe30f, R.drawable.emoji_1f48a);\n        sSoftbanksMap.append(0xe310, R.drawable.emoji_1f388);\n        sSoftbanksMap.append(0xe311, R.drawable.emoji_1f4a3);\n        sSoftbanksMap.append(0xe312, R.drawable.emoji_1f389);\n        sSoftbanksMap.append(0xe313, R.drawable.emoji_2702);\n        sSoftbanksMap.append(0xe314, R.drawable.emoji_1f380);\n        sSoftbanksMap.append(0xe315, R.drawable.emoji_3299);\n        sSoftbanksMap.append(0xe316, R.drawable.emoji_1f4bd);\n        sSoftbanksMap.append(0xe317, R.drawable.emoji_1f4e3);\n        sSoftbanksMap.append(0xe318, R.drawable.emoji_1f452);\n        sSoftbanksMap.append(0xe319, R.drawable.emoji_1f457);\n        sSoftbanksMap.append(0xe31a, R.drawable.emoji_1f461);\n        sSoftbanksMap.append(0xe31b, R.drawable.emoji_1f462);\n        sSoftbanksMap.append(0xe31c, R.drawable.emoji_1f484);\n        sSoftbanksMap.append(0xe31d, R.drawable.emoji_1f485);\n        sSoftbanksMap.append(0xe31e, R.drawable.emoji_1f486);\n        sSoftbanksMap.append(0xe31f, R.drawable.emoji_1f487);\n        sSoftbanksMap.append(0xe320, R.drawable.emoji_1f488);\n        sSoftbanksMap.append(0xe321, R.drawable.emoji_1f458);\n        sSoftbanksMap.append(0xe322, R.drawable.emoji_1f459);\n        sSoftbanksMap.append(0xe323, R.drawable.emoji_1f45c);\n        sSoftbanksMap.append(0xe324, R.drawable.emoji_1f3ac);\n        sSoftbanksMap.append(0xe325, R.drawable.emoji_1f514);\n        sSoftbanksMap.append(0xe326, R.drawable.emoji_1f3b6);\n        sSoftbanksMap.append(0xe327, R.drawable.emoji_1f493);\n        sSoftbanksMap.append(0xe328, R.drawable.emoji_1f48c);\n        sSoftbanksMap.append(0xe329, R.drawable.emoji_1f498);\n        sSoftbanksMap.append(0xe32a, R.drawable.emoji_1f499);\n        sSoftbanksMap.append(0xe32b, R.drawable.emoji_1f49a);\n        sSoftbanksMap.append(0xe32c, R.drawable.emoji_1f49b);\n        sSoftbanksMap.append(0xe32d, R.drawable.emoji_1f49c);\n        sSoftbanksMap.append(0xe32e, R.drawable.emoji_2728);\n        sSoftbanksMap.append(0xe32f, R.drawable.emoji_2b50);\n        sSoftbanksMap.append(0xe330, R.drawable.emoji_1f4a8);\n        sSoftbanksMap.append(0xe331, R.drawable.emoji_1f4a6);\n        sSoftbanksMap.append(0xe332, R.drawable.emoji_2b55);\n        sSoftbanksMap.append(0xe333, R.drawable.emoji_2716);\n        sSoftbanksMap.append(0xe334, R.drawable.emoji_1f4a2);\n        sSoftbanksMap.append(0xe335, R.drawable.emoji_1f31f);\n        sSoftbanksMap.append(0xe336, R.drawable.emoji_2754);\n        sSoftbanksMap.append(0xe337, R.drawable.emoji_2755);\n        sSoftbanksMap.append(0xe338, R.drawable.emoji_1f375);\n        sSoftbanksMap.append(0xe339, R.drawable.emoji_1f35e);\n        sSoftbanksMap.append(0xe33a, R.drawable.emoji_1f366);\n        sSoftbanksMap.append(0xe33b, R.drawable.emoji_1f35f);\n        sSoftbanksMap.append(0xe33c, R.drawable.emoji_1f361);\n        sSoftbanksMap.append(0xe33d, R.drawable.emoji_1f358);\n        sSoftbanksMap.append(0xe33e, R.drawable.emoji_1f35a);\n        sSoftbanksMap.append(0xe33f, R.drawable.emoji_1f35d);\n        sSoftbanksMap.append(0xe340, R.drawable.emoji_1f35c);\n        sSoftbanksMap.append(0xe341, R.drawable.emoji_1f35b);\n        sSoftbanksMap.append(0xe342, R.drawable.emoji_1f359);\n        sSoftbanksMap.append(0xe343, R.drawable.emoji_1f362);\n        sSoftbanksMap.append(0xe344, R.drawable.emoji_1f363);\n        sSoftbanksMap.append(0xe345, R.drawable.emoji_1f34e);\n        sSoftbanksMap.append(0xe346, R.drawable.emoji_1f34a);\n        sSoftbanksMap.append(0xe347, R.drawable.emoji_1f353);\n        sSoftbanksMap.append(0xe348, R.drawable.emoji_1f349);\n        sSoftbanksMap.append(0xe349, R.drawable.emoji_1f345);\n        sSoftbanksMap.append(0xe34a, R.drawable.emoji_1f346);\n        sSoftbanksMap.append(0xe34b, R.drawable.emoji_1f382);\n        sSoftbanksMap.append(0xe34c, R.drawable.emoji_1f371);\n        sSoftbanksMap.append(0xe34d, R.drawable.emoji_1f372);\n        sSoftbanksMap.append(0xe401, R.drawable.emoji_1f625);\n        sSoftbanksMap.append(0xe402, R.drawable.emoji_1f60f);\n        sSoftbanksMap.append(0xe403, R.drawable.emoji_1f614);\n        sSoftbanksMap.append(0xe404, R.drawable.emoji_1f601);\n        sSoftbanksMap.append(0xe405, R.drawable.emoji_1f609);\n        sSoftbanksMap.append(0xe406, R.drawable.emoji_1f623);\n        sSoftbanksMap.append(0xe407, R.drawable.emoji_1f616);\n        sSoftbanksMap.append(0xe408, R.drawable.emoji_1f62a);\n        sSoftbanksMap.append(0xe409, R.drawable.emoji_1f445);\n        sSoftbanksMap.append(0xe40a, R.drawable.emoji_1f606);\n        sSoftbanksMap.append(0xe40b, R.drawable.emoji_1f628);\n        sSoftbanksMap.append(0xe40c, R.drawable.emoji_1f637);\n        sSoftbanksMap.append(0xe40d, R.drawable.emoji_1f633);\n        sSoftbanksMap.append(0xe40e, R.drawable.emoji_1f612);\n        sSoftbanksMap.append(0xe40f, R.drawable.emoji_1f630);\n        sSoftbanksMap.append(0xe410, R.drawable.emoji_1f632);\n        sSoftbanksMap.append(0xe411, R.drawable.emoji_1f62d);\n        sSoftbanksMap.append(0xe412, R.drawable.emoji_1f602);\n        sSoftbanksMap.append(0xe413, R.drawable.emoji_1f622);\n        sSoftbanksMap.append(0xe414, R.drawable.emoji_263a);\n        sSoftbanksMap.append(0xe415, R.drawable.emoji_1f605);\n        sSoftbanksMap.append(0xe416, R.drawable.emoji_1f621);\n        sSoftbanksMap.append(0xe417, R.drawable.emoji_1f61a);\n        sSoftbanksMap.append(0xe418, R.drawable.emoji_1f618);\n        sSoftbanksMap.append(0xe419, R.drawable.emoji_1f440);\n        sSoftbanksMap.append(0xe41a, R.drawable.emoji_1f443);\n        sSoftbanksMap.append(0xe41b, R.drawable.emoji_1f442);\n        sSoftbanksMap.append(0xe41c, R.drawable.emoji_1f444);\n        sSoftbanksMap.append(0xe41d, R.drawable.emoji_1f64f);\n        sSoftbanksMap.append(0xe41e, R.drawable.emoji_1f44b);\n        sSoftbanksMap.append(0xe41f, R.drawable.emoji_1f44f);\n        sSoftbanksMap.append(0xe420, R.drawable.emoji_1f44c);\n        sSoftbanksMap.append(0xe421, R.drawable.emoji_1f44e);\n        sSoftbanksMap.append(0xe422, R.drawable.emoji_1f450);\n        sSoftbanksMap.append(0xe423, R.drawable.emoji_1f645);\n        sSoftbanksMap.append(0xe424, R.drawable.emoji_1f646);\n        sSoftbanksMap.append(0xe425, R.drawable.emoji_1f491);\n        sSoftbanksMap.append(0xe426, R.drawable.emoji_1f647);\n        sSoftbanksMap.append(0xe427, R.drawable.emoji_1f64c);\n        sSoftbanksMap.append(0xe428, R.drawable.emoji_1f46b);\n        sSoftbanksMap.append(0xe429, R.drawable.emoji_1f46f);\n        sSoftbanksMap.append(0xe42a, R.drawable.emoji_1f3c0);\n        sSoftbanksMap.append(0xe42b, R.drawable.emoji_1f3c8);\n        sSoftbanksMap.append(0xe42c, R.drawable.emoji_1f3b1);\n        sSoftbanksMap.append(0xe42d, R.drawable.emoji_1f3ca);\n        sSoftbanksMap.append(0xe42e, R.drawable.emoji_1f699);\n        sSoftbanksMap.append(0xe42f, R.drawable.emoji_1f69a);\n        sSoftbanksMap.append(0xe430, R.drawable.emoji_1f692);\n        sSoftbanksMap.append(0xe431, R.drawable.emoji_1f691);\n        sSoftbanksMap.append(0xe432, R.drawable.emoji_1f693);\n        sSoftbanksMap.append(0xe433, R.drawable.emoji_1f3a2);\n        sSoftbanksMap.append(0xe434, R.drawable.emoji_1f687);\n        sSoftbanksMap.append(0xe435, R.drawable.emoji_1f684);\n        sSoftbanksMap.append(0xe436, R.drawable.emoji_1f38d);\n        sSoftbanksMap.append(0xe437, R.drawable.emoji_1f49d);\n        sSoftbanksMap.append(0xe438, R.drawable.emoji_1f38e);\n        sSoftbanksMap.append(0xe439, R.drawable.emoji_1f393);\n        sSoftbanksMap.append(0xe43a, R.drawable.emoji_1f392);\n        sSoftbanksMap.append(0xe43b, R.drawable.emoji_1f38f);\n        sSoftbanksMap.append(0xe43c, R.drawable.emoji_1f302);\n        sSoftbanksMap.append(0xe43d, R.drawable.emoji_1f492);\n        sSoftbanksMap.append(0xe43e, R.drawable.emoji_1f30a);\n        sSoftbanksMap.append(0xe43f, R.drawable.emoji_1f367);\n        sSoftbanksMap.append(0xe440, R.drawable.emoji_1f387);\n        sSoftbanksMap.append(0xe441, R.drawable.emoji_1f41a);\n        sSoftbanksMap.append(0xe442, R.drawable.emoji_1f390);\n        sSoftbanksMap.append(0xe443, R.drawable.emoji_1f300);\n        sSoftbanksMap.append(0xe444, R.drawable.emoji_1f33e);\n        sSoftbanksMap.append(0xe445, R.drawable.emoji_1f383);\n        sSoftbanksMap.append(0xe446, R.drawable.emoji_1f391);\n        sSoftbanksMap.append(0xe447, R.drawable.emoji_1f343);\n        sSoftbanksMap.append(0xe448, R.drawable.emoji_1f385);\n        sSoftbanksMap.append(0xe449, R.drawable.emoji_1f305);\n        sSoftbanksMap.append(0xe44a, R.drawable.emoji_1f307);\n        sSoftbanksMap.append(0xe44b, R.drawable.emoji_1f303);\n        sSoftbanksMap.append(0xe44b, R.drawable.emoji_1f30c);\n        sSoftbanksMap.append(0xe44c, R.drawable.emoji_1f308);\n        sSoftbanksMap.append(0xe501, R.drawable.emoji_1f3e9);\n        sSoftbanksMap.append(0xe502, R.drawable.emoji_1f3a8);\n        sSoftbanksMap.append(0xe503, R.drawable.emoji_1f3a9);\n        sSoftbanksMap.append(0xe504, R.drawable.emoji_1f3ec);\n        sSoftbanksMap.append(0xe505, R.drawable.emoji_1f3ef);\n        sSoftbanksMap.append(0xe506, R.drawable.emoji_1f3f0);\n        sSoftbanksMap.append(0xe507, R.drawable.emoji_1f3a6);\n        sSoftbanksMap.append(0xe508, R.drawable.emoji_1f3ed);\n        sSoftbanksMap.append(0xe509, R.drawable.emoji_1f5fc);\n        sSoftbanksMap.append(0xe50b, R.drawable.emoji_1f1ef_1f1f5);\n        sSoftbanksMap.append(0xe50c, R.drawable.emoji_1f1fa_1f1f8);\n        sSoftbanksMap.append(0xe50d, R.drawable.emoji_1f1eb_1f1f7);\n        sSoftbanksMap.append(0xe50e, R.drawable.emoji_1f1e9_1f1ea);\n        sSoftbanksMap.append(0xe50f, R.drawable.emoji_1f1ee_1f1f9);\n        sSoftbanksMap.append(0xe510, R.drawable.emoji_1f1ec_1f1e7);\n        sSoftbanksMap.append(0xe511, R.drawable.emoji_1f1ea_1f1f8);\n        sSoftbanksMap.append(0xe512, R.drawable.emoji_1f1f7_1f1fa);\n        sSoftbanksMap.append(0xe513, R.drawable.emoji_1f1e8_1f1f3);\n        sSoftbanksMap.append(0xe514, R.drawable.emoji_1f1f0_1f1f7);\n        sSoftbanksMap.append(0xe515, R.drawable.emoji_1f471);\n        sSoftbanksMap.append(0xe516, R.drawable.emoji_1f472);\n        sSoftbanksMap.append(0xe517, R.drawable.emoji_1f473);\n        sSoftbanksMap.append(0xe518, R.drawable.emoji_1f474);\n        sSoftbanksMap.append(0xe519, R.drawable.emoji_1f475);\n        sSoftbanksMap.append(0xe51a, R.drawable.emoji_1f476);\n        sSoftbanksMap.append(0xe51b, R.drawable.emoji_1f477);\n        sSoftbanksMap.append(0xe51c, R.drawable.emoji_1f478);\n        sSoftbanksMap.append(0xe51d, R.drawable.emoji_1f5fd);\n        sSoftbanksMap.append(0xe51e, R.drawable.emoji_1f482);\n        sSoftbanksMap.append(0xe51f, R.drawable.emoji_1f483);\n        sSoftbanksMap.append(0xe520, R.drawable.emoji_1f42c);\n        sSoftbanksMap.append(0xe521, R.drawable.emoji_1f426);\n        sSoftbanksMap.append(0xe522, R.drawable.emoji_1f420);\n        sSoftbanksMap.append(0xe523, R.drawable.emoji_1f423);\n        sSoftbanksMap.append(0xe524, R.drawable.emoji_1f439);\n        sSoftbanksMap.append(0xe525, R.drawable.emoji_1f41b);\n        sSoftbanksMap.append(0xe526, R.drawable.emoji_1f418);\n        sSoftbanksMap.append(0xe527, R.drawable.emoji_1f428);\n        sSoftbanksMap.append(0xe528, R.drawable.emoji_1f412);\n        sSoftbanksMap.append(0xe529, R.drawable.emoji_1f411);\n        sSoftbanksMap.append(0xe52a, R.drawable.emoji_1f43a);\n        sSoftbanksMap.append(0xe52b, R.drawable.emoji_1f42e);\n        sSoftbanksMap.append(0xe52c, R.drawable.emoji_1f430);\n        sSoftbanksMap.append(0xe52d, R.drawable.emoji_1f40d);\n        sSoftbanksMap.append(0xe52e, R.drawable.emoji_1f414);\n        sSoftbanksMap.append(0xe52f, R.drawable.emoji_1f417);\n        sSoftbanksMap.append(0xe530, R.drawable.emoji_1f42b);\n        sSoftbanksMap.append(0xe531, R.drawable.emoji_1f438);\n        sSoftbanksMap.append(0xe532, R.drawable.emoji_1f170);\n        sSoftbanksMap.append(0xe533, R.drawable.emoji_1f171);\n        sSoftbanksMap.append(0xe534, R.drawable.emoji_1f18e);\n        sSoftbanksMap.append(0xe535, R.drawable.emoji_1f17e);\n        sSoftbanksMap.append(0xe536, R.drawable.emoji_1f43e);\n        sSoftbanksMap.append(0xe537, R.drawable.emoji_2122);\n\n        Log.d(\"emoji\", String.format(\"init emoji cost: %dms\", (System.currentTimeMillis() - start)));\n    }\n\n    private EmojiconHandler() {\n    }\n\n    private static boolean isSoftBankEmoji(char c) {\n        return ((c >> 12) == 0xe);\n    }\n\n    private static int getEmojiResource(int codePoint) {\n        return sEmojisMap.get(codePoint);\n    }\n\n    private static int getSoftbankEmojiResource(char c) {\n        return sSoftbanksMap.get(c);\n    }\n\n    /**\n     * @param context   Convert emoji characters of the given Spannable to the according emojicon.\n     * @param text\n     * @param emojiSize\n     */\n    public static void addEmojis(Context context, SpannableStringBuilder text, int emojiSize, int textSize) {\n        addEmojis(context, text, emojiSize, textSize, 0, -1, false);\n    }\n\n    /**\n     * Convert emoji characters of the given Spannable to the according emojicon.\n     *\n     * @param context\n     * @param text\n     * @param emojiSize\n     * @param index\n     * @param length\n     */\n    public static void addEmojis(Context context, SpannableStringBuilder text, int emojiSize, int textSize, int index, int length) {\n        addEmojis(context, text, emojiSize, textSize, index, length, false);\n    }\n\n    /**\n     * Convert emoji characters of the given Spannable to the according emojicon.\n     *\n     * @param context\n     * @param text\n     * @param emojiSize\n     * @param useSystemDefault\n     */\n    public static void addEmojis(Context context, SpannableStringBuilder text, int emojiSize, int textSize, boolean useSystemDefault) {\n        addEmojis(context, text, emojiSize, textSize, 0, -1, useSystemDefault);\n    }\n\n    /**\n     * Convert emoji characters of the given Spannable to the according emojicon.\n     *\n     * @param context\n     * @param text\n     * @param emojiSize\n     * @param index\n     * @param length\n     * @param useSystemDefault\n     */\n    public static SpannableStringBuilder addEmojis(Context context, SpannableStringBuilder text, int emojiSize, int textSize, int index, int length, boolean useSystemDefault) {\n        if (useSystemDefault) {\n            return text;\n        }\n\n        int textLengthToProcess = calculateLegalTextLength(text, index, length);\n\n        // remove spans throughout all text\n        EmojiconSpan[] oldSpans = text.getSpans(0, text.length(), EmojiconSpan.class);\n        for (EmojiconSpan oldSpan : oldSpans) {\n            text.removeSpan(oldSpan);\n        }\n\n        int[] results = new int[3];\n        String textStr = text.toString();\n\n        int processIdx = index;\n        while (processIdx < textLengthToProcess) {\n\n            boolean isEmoji = findEmoji(textStr, processIdx, textLengthToProcess, results);\n            int skip = results[1];\n            if (isEmoji) {\n                int icon = results[0];\n                boolean isQQFace = results[2] > 0;\n                EmojiconSpan span = new EmojiconSpan(context, icon, (int) (emojiSize * EMOJIICON_SCALE),\n                        (int) (emojiSize * EMOJIICON_SCALE));\n                span.setTranslateY(isQQFace ? QQFACE_TRANSLATE_Y : EMOJIICON_TRANSLATE_Y);\n                if (span.getCachedDrawable() == null) {\n                    text.replace(processIdx, processIdx + skip, \"..\");\n                    //重新计算字符串的合法长度\n                    textLengthToProcess = calculateLegalTextLength(text, index, length);\n                } else {\n                    text.setSpan(span, processIdx, processIdx + skip, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            }\n\n            processIdx += skip;\n        }\n        return (SpannableStringBuilder) text.subSequence(index, processIdx);\n    }\n\n    /**\n     * 判断文本位于start的字节是否为emoji。\n     *\n     * @param text\n     * @param start\n     * @param end\n     * @param result 长度为3的数据。当第一位表示emoji的资源id，\n     *               第二位表示emoji在原文本占位长度，\n     *               第三位表示emoji类型是否位qq表情。\n     * @return 如果是emoji，返回True。\n     */\n    public static boolean findEmoji(String text, int start, int end, int[] result) {\n        int skip = 0;\n        int icon = 0;\n        char c = text.charAt(start);\n        if (isSoftBankEmoji(c)) {\n            icon = getSoftbankEmojiResource(c);\n            skip = icon == 0 ? 0 : 1;\n        }\n\n        if (icon == 0) {\n            int unicode = Character.codePointAt(text, start);\n            skip = Character.charCount(unicode);\n\n            if (unicode > 0xff) {\n                icon = getEmojiResource(unicode);\n            }\n\n            if (icon == 0 && start + skip < end) {\n                int followUnicode = Character.codePointAt(text, start + skip);\n                if (followUnicode == 0x20e3) {\n                    int followSkip = Character.charCount(followUnicode);\n                    switch (unicode) {\n                        case 0x0031:\n                            icon = R.drawable.emoji_0031;\n                            break;\n                        case 0x0032:\n                            icon = R.drawable.emoji_0032;\n                            break;\n                        case 0x0033:\n                            icon = R.drawable.emoji_0033;\n                            break;\n                        case 0x0034:\n                            icon = R.drawable.emoji_0034;\n                            break;\n                        case 0x0035:\n                            icon = R.drawable.emoji_0035;\n                            break;\n                        case 0x0036:\n                            icon = R.drawable.emoji_0036;\n                            break;\n                        case 0x0037:\n                            icon = R.drawable.emoji_0037;\n                            break;\n                        case 0x0038:\n                            icon = R.drawable.emoji_0038;\n                            break;\n                        case 0x0039:\n                            icon = R.drawable.emoji_0039;\n                            break;\n                        case 0x0030:\n                            icon = R.drawable.emoji_0030;\n                            break;\n                        case 0x0023:\n                            icon = R.drawable.emoji_0023;\n                            break;\n                        default:\n                            followSkip = 0;\n                            break;\n                    }\n                    skip += followSkip;\n                } else {\n                    int followSkip = Character.charCount(followUnicode);\n                    switch (unicode) {\n                        case 0x1f1ef:\n                            icon = (followUnicode == 0x1f1f5) ? R.drawable.emoji_1f1ef_1f1f5 : 0;\n                            break;\n                        case 0x1f1fa:\n                            icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1fa_1f1f8 : 0;\n                            break;\n                        case 0x1f1eb:\n                            icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1eb_1f1f7 : 0;\n                            break;\n                        case 0x1f1e9:\n                            icon = (followUnicode == 0x1f1ea) ? R.drawable.emoji_1f1e9_1f1ea : 0;\n                            break;\n                        case 0x1f1ee:\n                            icon = (followUnicode == 0x1f1f9) ? R.drawable.emoji_1f1ee_1f1f9 : 0;\n                            break;\n                        case 0x1f1ec:\n                            icon = (followUnicode == 0x1f1e7) ? R.drawable.emoji_1f1ec_1f1e7 : 0;\n                            break;\n                        case 0x1f1ea:\n                            icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1ea_1f1f8 : 0;\n                            break;\n                        case 0x1f1f7:\n                            icon = (followUnicode == 0x1f1fa) ? R.drawable.emoji_1f1f7_1f1fa : 0;\n                            break;\n                        case 0x1f1e8:\n                            icon = (followUnicode == 0x1f1f3) ? R.drawable.emoji_1f1e8_1f1f3 : 0;\n                            break;\n                        case 0x1f1f0:\n                            icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1f0_1f1f7 : 0;\n                            break;\n                        default:\n                            followSkip = 0;\n                            break;\n                    }\n                    skip += followSkip;\n                }\n            }\n        }\n\n        boolean isQQFace = false;\n        if (icon == 0) {\n            if (c == '[') {\n                int emojiCloseIndex = text.indexOf(']', start);\n                if (emojiCloseIndex > 0 && emojiCloseIndex - start <= 4) {\n                    CharSequence charSequence = text.subSequence(start, emojiCloseIndex + 1);\n                    Integer value = sQQFaceMap.get(charSequence.toString());\n\n                    if (value != null) {\n                        icon = value;\n                        skip = emojiCloseIndex + 1 - start;\n                        isQQFace = true;\n                    }\n                }\n            }\n        }\n\n        result[0] = icon;\n        result[1] = skip;\n        result[2] = isQQFace ? 1 : 0;\n\n        return icon > 0;\n    }\n\n    public static String findQQFaceFileName(String key) {\n        return mQQFaceFileNameList.get(key);\n    }\n\n    private static int calculateLegalTextLength(SpannableStringBuilder text, int index, int length) {\n        int textLength = text.length();\n        int textLengthToProcessMax = textLength - index;\n        return (length < 0 || length >= textLengthToProcessMax ? textLength : (length + index));\n    }\n\n    public static List<QQFace> getQQFaceKeyList() {\n        return mQQFaceList;\n    }\n\n    public static boolean isQQFaceCodeExist(String qqFaceCode) {\n        return sQQFaceMap.get(qqFaceCode) != null;\n    }\n\n    public static class QQFace {\n        private String name;\n        private int res;\n\n        public QQFace(String name, int res) {\n            this.name = name;\n            this.res = res;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public int getRes() {\n            return res;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/EmojiconSpan.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.text.style.DynamicDrawableSpan;\n\nimport java.lang.ref.WeakReference;\n\n\nclass EmojiconSpan extends DynamicDrawableSpan {\n\n    private final Context mContext;\n\n    private final int mResourceId;\n\n    private final int mSize;\n\n    private final int mTextSize;\n\n    private int mHeight;\n\n    private int mWidth;\n\n    private int mTop;\n\n    private Drawable mDrawable;\n\n    private WeakReference<Drawable> mDrawableRef;\n\n    // 手动偏移值\n    private int mTranslateY = 0;\n\n    public EmojiconSpan(Context context, int resourceId, int size, int textSize) {\n        super(DynamicDrawableSpan.ALIGN_BASELINE);\n        mContext = context;\n        mResourceId = resourceId;\n        mWidth = mHeight = mSize = size;\n        mTextSize = textSize;\n    }\n\n    @Override\n    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {\n//        return super.getSize(paint, text, start, end, fm);\n\n        // fm 以 Paint 的 fm 为基准，避免 Span 改变了 fm 导致文字行高变化 -- chant\n        Drawable d = getCachedDrawable();\n        Rect rect = d.getBounds();\n\n        if (fm != null) {\n            Paint.FontMetricsInt pfm = paint.getFontMetricsInt();\n            // keep it the same as paint's fm\n            fm.ascent = pfm.ascent;\n            fm.descent = pfm.descent;\n            fm.top = pfm.top;\n            fm.bottom = pfm.bottom;\n        }\n\n        return rect.right;\n    }\n\n    public Drawable getDrawable() {\n        if (mDrawable == null) {\n            try {\n            \tmDrawable = EmojiCache.getInstance().getDrawable(mContext, mResourceId);\n                if (mDrawable != null) {\n                \tmHeight = mSize;\n                    mWidth = mHeight * mDrawable.getIntrinsicWidth() / mDrawable.getIntrinsicHeight();\n                    mTop = (mTextSize - mHeight) / 2;\n                    mDrawable.setBounds(0, mTop, mWidth, mTop + mHeight);\n\t\t\t\t}\n            } catch (Exception e) {\n                // swallow\n            }\n        }\n        return mDrawable;\n    }\n\n    @Override\n    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {\n        //super.draw(canvas, text, start, end, x, top, y, bottom, paint);\n        Drawable b = getCachedDrawable();\n        int count = canvas.save();\n\n//        int transY = fontMetricesBottom - b.getBounds().bottom;\n//        int transY -= paint.getFontMetricsInt().descent;\n\n        // 因为 TextView 加了 lineSpacing 之后会导致这里的 bottom、top 参数与单行情况不一样，所以不用 bottom、top，而使用 fontMetrics 的高度来计算\n        int fontMetricesTop = y + paint.getFontMetricsInt().top;\n        int fontMetricesBottom = fontMetricesTop + (paint.getFontMetricsInt().bottom - paint.getFontMetricsInt().top);\n        int transY = fontMetricesTop + ((fontMetricesBottom - fontMetricesTop) / 2) - ((b.getBounds().bottom - b.getBounds().top) / 2) - mTop;\n\n        transY += mTranslateY;\n\n        canvas.translate(x, transY);\n        b.draw(canvas);\n        canvas.restoreToCount(count);\n    }\n\n    public Drawable getCachedDrawable() {\n        if (mDrawableRef == null || mDrawableRef.get() == null) {\n            mDrawableRef = new WeakReference<>(getDrawable());\n        }\n        return mDrawableRef.get();\n    }\n\n    public void setTranslateY(int translateY) {\n        mTranslateY = translateY;\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/EmojiconTextView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon;\n\nimport android.content.Context;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\n\nimport com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView;\n\n\npublic class EmojiconTextView extends QMUISpanTouchFixTextView {\n    private int mEmojiconSize;\n    private int mEmojiconTextSize;\n    private int mTextStart = 0;\n    private int mTextLength = -1;\n    private boolean mUseSystemDefault = false;\n\n    public EmojiconTextView(Context context) {\n        super(context);\n        init(null);\n    }\n\n    public EmojiconTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(attrs);\n    }\n\n    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(attrs);\n    }\n\n    private void init(AttributeSet attrs) {\n        mEmojiconTextSize = (int) getTextSize();\n        mEmojiconSize = (int) getTextSize();\n        setText(getText());\n    }\n\n    public void setTextWithWidth(CharSequence text, int limitedWidth) {\n        if (TextUtils.isEmpty(text)) {\n            super.setText(text);\n            return;\n        }\n        if (limitedWidth < 0) {\n            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();\n        }\n\n        SpannableStringBuilder builder = new SpannableStringBuilder(text);\n        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);\n        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());\n        super.setText(trucatedText, BufferType.SPANNABLE);\n    }\n\n    @Override\n    public void setText(CharSequence text, BufferType type) {\n        if (!TextUtils.isEmpty(text)) {\n            SpannableStringBuilder builder = new SpannableStringBuilder(text);\n            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);\n            text = builder;\n        }\n        super.setText(text, type);\n    }\n\n\n    /**\n     * Set the size of emojicon in pixels.\n     */\n    public void setEmojiconSize(int pixels) {\n        mEmojiconSize = pixels;\n        super.setText(getText());\n    }\n\n    /**\n     * Set whether to use system default emojicon\n     */\n    public void setUseSystemDefault(boolean useSystemDefault) {\n        mUseSystemDefault = useSystemDefault;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/Emojicon.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\nimport java.io.Serializable;\n\npublic class Emojicon implements Serializable {\n    private static final long serialVersionUID = 1L;\n    private int icon;\n    private char value;\n    private String emoji;\n\n    private Emojicon() {\n    }\n\n    public static Emojicon fromResource(int icon, int value) {\n        Emojicon emoji = new Emojicon();\n        emoji.icon = icon;\n        emoji.value = (char) value;\n        return emoji;\n    }\n\n    public static Emojicon fromCodePoint(int codePoint) {\n        Emojicon emoji = new Emojicon();\n        emoji.emoji = newString(codePoint);\n        return emoji;\n    }\n\n    public static Emojicon fromChar(char ch) {\n        Emojicon emoji = new Emojicon();\n        emoji.emoji = Character.toString(ch);\n        return emoji;\n    }\n\n    public static Emojicon fromChars(String chars) {\n        Emojicon emoji = new Emojicon();\n        emoji.emoji = chars;\n        return emoji;\n    }\n\n    public Emojicon(String emoji) {\n        this.emoji = emoji;\n    }\n\n    public char getValue() {\n        return value;\n    }\n\n    public int getIcon() {\n        return icon;\n    }\n\n    public String getEmoji() {\n        return emoji;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        return o instanceof Emojicon && emoji.equals(((Emojicon) o).emoji);\n    }\n\n    @Override\n    public int hashCode() {\n        return emoji.hashCode();\n    }\n\n    public static String newString(int codePoint) {\n    \t// Character.charCount 指定字符是否是等于或大于0x10000的，那么该方法返回2。否则，该方法返回1。\n        if (Character.charCount(codePoint) == 1) {\n            return String.valueOf(codePoint);\n        } else {\n        \t//指定字符（Unicode代码点）转换成UTF-16表示存储在一个char数组。\n            return new String(Character.toChars(codePoint));\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/Nature.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\n\npublic class Nature {\n    public static final Emojicon[] DATA = new Emojicon[]{\n            Emojicon.fromCodePoint(0x1f436),\n            Emojicon.fromCodePoint(0x1f43a),\n            Emojicon.fromCodePoint(0x1f431),\n            Emojicon.fromCodePoint(0x1f42d),\n            Emojicon.fromCodePoint(0x1f439),\n            Emojicon.fromCodePoint(0x1f430),\n            Emojicon.fromCodePoint(0x1f438),\n            Emojicon.fromCodePoint(0x1f42f),\n            Emojicon.fromCodePoint(0x1f428),\n            Emojicon.fromCodePoint(0x1f43b),\n            Emojicon.fromCodePoint(0x1f437),\n            Emojicon.fromCodePoint(0x1f43d),\n            Emojicon.fromCodePoint(0x1f42e),\n            Emojicon.fromCodePoint(0x1f417),\n            Emojicon.fromCodePoint(0x1f435),\n            Emojicon.fromCodePoint(0x1f412),\n            Emojicon.fromCodePoint(0x1f434),\n            Emojicon.fromCodePoint(0x1f411),\n            Emojicon.fromCodePoint(0x1f418),\n            Emojicon.fromCodePoint(0x1f43c),\n            Emojicon.fromCodePoint(0x1f427),\n            Emojicon.fromCodePoint(0x1f426),\n            Emojicon.fromCodePoint(0x1f424),\n            Emojicon.fromCodePoint(0x1f425),\n            Emojicon.fromCodePoint(0x1f423),\n            Emojicon.fromCodePoint(0x1f414),\n            Emojicon.fromCodePoint(0x1f40d),\n            Emojicon.fromCodePoint(0x1f422),\n            Emojicon.fromCodePoint(0x1f41b),\n            Emojicon.fromCodePoint(0x1f41d),\n            Emojicon.fromCodePoint(0x1f41c),\n            Emojicon.fromCodePoint(0x1f41e),\n            Emojicon.fromCodePoint(0x1f40c),\n            Emojicon.fromCodePoint(0x1f419),\n            Emojicon.fromCodePoint(0x1f41a),\n            Emojicon.fromCodePoint(0x1f420),\n            Emojicon.fromCodePoint(0x1f41f),\n            Emojicon.fromCodePoint(0x1f42c),\n            Emojicon.fromCodePoint(0x1f433),\n            Emojicon.fromCodePoint(0x1f40b),\n            Emojicon.fromCodePoint(0x1f404),\n            Emojicon.fromCodePoint(0x1f40f),\n            Emojicon.fromCodePoint(0x1f400),\n            Emojicon.fromCodePoint(0x1f403),\n            Emojicon.fromCodePoint(0x1f405),\n            Emojicon.fromCodePoint(0x1f407),\n            Emojicon.fromCodePoint(0x1f409),\n            Emojicon.fromCodePoint(0x1f40e),\n            Emojicon.fromCodePoint(0x1f410),\n            Emojicon.fromCodePoint(0x1f413),\n            Emojicon.fromCodePoint(0x1f415),\n            Emojicon.fromCodePoint(0x1f416),\n            Emojicon.fromCodePoint(0x1f401),\n            Emojicon.fromCodePoint(0x1f402),\n            Emojicon.fromCodePoint(0x1f432),\n            Emojicon.fromCodePoint(0x1f421),\n            Emojicon.fromCodePoint(0x1f40a),\n            Emojicon.fromCodePoint(0x1f42b),\n            Emojicon.fromCodePoint(0x1f42a),\n            Emojicon.fromCodePoint(0x1f406),\n            Emojicon.fromCodePoint(0x1f408),\n            Emojicon.fromCodePoint(0x1f429),\n            Emojicon.fromCodePoint(0x1f43e),\n            Emojicon.fromCodePoint(0x1f490),\n            Emojicon.fromCodePoint(0x1f338),\n            Emojicon.fromCodePoint(0x1f337),\n            Emojicon.fromCodePoint(0x1f340),\n            Emojicon.fromCodePoint(0x1f339),\n            Emojicon.fromCodePoint(0x1f33b),\n            Emojicon.fromCodePoint(0x1f33a),\n            Emojicon.fromCodePoint(0x1f341),\n            Emojicon.fromCodePoint(0x1f343),\n            Emojicon.fromCodePoint(0x1f342),\n            Emojicon.fromCodePoint(0x1f33f),\n            Emojicon.fromCodePoint(0x1f33e),\n            Emojicon.fromCodePoint(0x1f344),\n            Emojicon.fromCodePoint(0x1f335),\n            Emojicon.fromCodePoint(0x1f334),\n            Emojicon.fromCodePoint(0x1f332),\n            Emojicon.fromCodePoint(0x1f333),\n            Emojicon.fromCodePoint(0x1f330),\n            Emojicon.fromCodePoint(0x1f331),\n            Emojicon.fromCodePoint(0x1f33c),\n            Emojicon.fromCodePoint(0x1f310),\n            Emojicon.fromCodePoint(0x1f31e),\n            Emojicon.fromCodePoint(0x1f31d),\n            Emojicon.fromCodePoint(0x1f31a),\n            Emojicon.fromCodePoint(0x1f311),\n            Emojicon.fromCodePoint(0x1f312),\n            Emojicon.fromCodePoint(0x1f313),\n            Emojicon.fromCodePoint(0x1f314),\n            Emojicon.fromCodePoint(0x1f315),\n            Emojicon.fromCodePoint(0x1f316),\n            Emojicon.fromCodePoint(0x1f317),\n            Emojicon.fromCodePoint(0x1f318),\n            Emojicon.fromCodePoint(0x1f31c),\n            Emojicon.fromCodePoint(0x1f31b),\n            Emojicon.fromCodePoint(0x1f319),\n            Emojicon.fromCodePoint(0x1f30d),\n            Emojicon.fromCodePoint(0x1f30e),\n            Emojicon.fromCodePoint(0x1f30f),\n            Emojicon.fromCodePoint(0x1f30b),\n            Emojicon.fromCodePoint(0x1f30c),\n            Emojicon.fromCodePoint(0x1f320),\n            Emojicon.fromChar((char) 0x2b50),\n            Emojicon.fromChar((char) 0x2600),\n            Emojicon.fromChar((char) 0x26c5),\n            Emojicon.fromChar((char) 0x2601),\n            Emojicon.fromChar((char) 0x26a1),\n            Emojicon.fromChar((char) 0x2614),\n            Emojicon.fromChar((char) 0x2744),\n            Emojicon.fromChar((char) 0x26c4),\n            Emojicon.fromCodePoint(0x1f300),\n            Emojicon.fromCodePoint(0x1f301),\n            Emojicon.fromCodePoint(0x1f308),\n            Emojicon.fromCodePoint(0x1f30a),\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/Objects.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\n\npublic class Objects {\n    public static final Emojicon[] DATA = new Emojicon[]{\n            Emojicon.fromCodePoint(0x1f38d),\n            Emojicon.fromCodePoint(0x1f49d),\n            Emojicon.fromCodePoint(0x1f38e),\n            Emojicon.fromCodePoint(0x1f392),\n            Emojicon.fromCodePoint(0x1f393),\n            Emojicon.fromCodePoint(0x1f38f),\n            Emojicon.fromCodePoint(0x1f386),\n            Emojicon.fromCodePoint(0x1f387),\n            Emojicon.fromCodePoint(0x1f390),\n            Emojicon.fromCodePoint(0x1f391),\n            Emojicon.fromCodePoint(0x1f383),\n            Emojicon.fromCodePoint(0x1f47b),\n            Emojicon.fromCodePoint(0x1f385),\n            Emojicon.fromCodePoint(0x1f384),\n            Emojicon.fromCodePoint(0x1f381),\n            Emojicon.fromCodePoint(0x1f38b),\n            Emojicon.fromCodePoint(0x1f389),\n            Emojicon.fromCodePoint(0x1f38a),\n            Emojicon.fromCodePoint(0x1f388),\n            Emojicon.fromCodePoint(0x1f38c),\n            Emojicon.fromCodePoint(0x1f52e),\n            Emojicon.fromCodePoint(0x1f3a5),\n            Emojicon.fromCodePoint(0x1f4f7),\n            Emojicon.fromCodePoint(0x1f4f9),\n            Emojicon.fromCodePoint(0x1f4fc),\n            Emojicon.fromCodePoint(0x1f4bf),\n            Emojicon.fromCodePoint(0x1f4c0),\n            Emojicon.fromCodePoint(0x1f4bd),\n            Emojicon.fromCodePoint(0x1f4be),\n            Emojicon.fromCodePoint(0x1f4bb),\n            Emojicon.fromCodePoint(0x1f4f1),\n            Emojicon.fromChar((char) 0x260e),\n            Emojicon.fromCodePoint(0x1f4de),\n            Emojicon.fromCodePoint(0x1f4df),\n            Emojicon.fromCodePoint(0x1f4e0),\n            Emojicon.fromCodePoint(0x1f4e1),\n            Emojicon.fromCodePoint(0x1f4fa),\n            Emojicon.fromCodePoint(0x1f4fb),\n            Emojicon.fromCodePoint(0x1f508),\n            Emojicon.fromCodePoint(0x1f509),\n            Emojicon.fromCodePoint(0x1f50a),\n            Emojicon.fromCodePoint(0x1f507),\n            Emojicon.fromCodePoint(0x1f514),\n            Emojicon.fromCodePoint(0x1f515),\n            Emojicon.fromCodePoint(0x1f4e2),\n            Emojicon.fromCodePoint(0x1f4e3),\n            Emojicon.fromChar((char) 0x23f3),\n            Emojicon.fromChar((char) 0x231b),\n            Emojicon.fromChar((char) 0x23f0),\n            Emojicon.fromChar((char) 0x231a),\n            Emojicon.fromCodePoint(0x1f513),\n            Emojicon.fromCodePoint(0x1f512),\n            Emojicon.fromCodePoint(0x1f50f),\n            Emojicon.fromCodePoint(0x1f510),\n            Emojicon.fromCodePoint(0x1f511),\n            Emojicon.fromCodePoint(0x1f50e),\n            Emojicon.fromCodePoint(0x1f4a1),\n            Emojicon.fromCodePoint(0x1f526),\n            Emojicon.fromCodePoint(0x1f506),\n            Emojicon.fromCodePoint(0x1f505),\n            Emojicon.fromCodePoint(0x1f50c),\n            Emojicon.fromCodePoint(0x1f50b),\n            Emojicon.fromCodePoint(0x1f50d),\n            Emojicon.fromCodePoint(0x1f6c1),\n            Emojicon.fromCodePoint(0x1f6c0),\n            Emojicon.fromCodePoint(0x1f6bf),\n            Emojicon.fromCodePoint(0x1f6bd),\n            Emojicon.fromCodePoint(0x1f527),\n            Emojicon.fromCodePoint(0x1f529),\n            Emojicon.fromCodePoint(0x1f528),\n            Emojicon.fromCodePoint(0x1f6aa),\n            Emojicon.fromCodePoint(0x1f6ac),\n            Emojicon.fromCodePoint(0x1f4a3),\n            Emojicon.fromCodePoint(0x1f52b),\n            Emojicon.fromCodePoint(0x1f52a),\n            Emojicon.fromCodePoint(0x1f48a),\n            Emojicon.fromCodePoint(0x1f489),\n            Emojicon.fromCodePoint(0x1f4b0),\n            Emojicon.fromCodePoint(0x1f4b4),\n            Emojicon.fromCodePoint(0x1f4b5),\n            Emojicon.fromCodePoint(0x1f4b7),\n            Emojicon.fromCodePoint(0x1f4b6),\n            Emojicon.fromCodePoint(0x1f4b3),\n            Emojicon.fromCodePoint(0x1f4b8),\n            Emojicon.fromCodePoint(0x1f4f2),\n            Emojicon.fromCodePoint(0x1f4e7),\n            Emojicon.fromCodePoint(0x1f4e5),\n            Emojicon.fromCodePoint(0x1f4e4),\n            Emojicon.fromChar((char) 0x2709),\n            Emojicon.fromCodePoint(0x1f4e9),\n            Emojicon.fromCodePoint(0x1f4e8),\n            Emojicon.fromCodePoint(0x1f4ef),\n            Emojicon.fromCodePoint(0x1f4eb),\n            Emojicon.fromCodePoint(0x1f4ea),\n            Emojicon.fromCodePoint(0x1f4ec),\n            Emojicon.fromCodePoint(0x1f4ed),\n            Emojicon.fromCodePoint(0x1f4ee),\n            Emojicon.fromCodePoint(0x1f4e6),\n            Emojicon.fromCodePoint(0x1f4dd),\n            Emojicon.fromCodePoint(0x1f4c4),\n            Emojicon.fromCodePoint(0x1f4c3),\n            Emojicon.fromCodePoint(0x1f4d1),\n            Emojicon.fromCodePoint(0x1f4ca),\n            Emojicon.fromCodePoint(0x1f4c8),\n            Emojicon.fromCodePoint(0x1f4c9),\n            Emojicon.fromCodePoint(0x1f4dc),\n            Emojicon.fromCodePoint(0x1f4cb),\n            Emojicon.fromCodePoint(0x1f4c5),\n            Emojicon.fromCodePoint(0x1f4c6),\n            Emojicon.fromCodePoint(0x1f4c7),\n            Emojicon.fromCodePoint(0x1f4c1),\n            Emojicon.fromCodePoint(0x1f4c2),\n            Emojicon.fromChar((char) 0x2702),\n            Emojicon.fromCodePoint(0x1f4cc),\n            Emojicon.fromCodePoint(0x1f4ce),\n            Emojicon.fromChar((char) 0x2712),\n            Emojicon.fromChar((char) 0x270f),\n            Emojicon.fromCodePoint(0x1f4cf),\n            Emojicon.fromCodePoint(0x1f4d0),\n            Emojicon.fromCodePoint(0x1f4d5),\n            Emojicon.fromCodePoint(0x1f4d7),\n            Emojicon.fromCodePoint(0x1f4d8),\n            Emojicon.fromCodePoint(0x1f4d9),\n            Emojicon.fromCodePoint(0x1f4d3),\n            Emojicon.fromCodePoint(0x1f4d4),\n            Emojicon.fromCodePoint(0x1f4d2),\n            Emojicon.fromCodePoint(0x1f4da),\n            Emojicon.fromCodePoint(0x1f4d6),\n            Emojicon.fromCodePoint(0x1f516),\n            Emojicon.fromCodePoint(0x1f4db),\n            Emojicon.fromCodePoint(0x1f52c),\n            Emojicon.fromCodePoint(0x1f52d),\n            Emojicon.fromCodePoint(0x1f4f0),\n            Emojicon.fromCodePoint(0x1f3a8),\n            Emojicon.fromCodePoint(0x1f3ac),\n            Emojicon.fromCodePoint(0x1f3a4),\n            Emojicon.fromCodePoint(0x1f3a7),\n            Emojicon.fromCodePoint(0x1f3bc),\n            Emojicon.fromCodePoint(0x1f3b5),\n            Emojicon.fromCodePoint(0x1f3b6),\n            Emojicon.fromCodePoint(0x1f3b9),\n            Emojicon.fromCodePoint(0x1f3bb),\n            Emojicon.fromCodePoint(0x1f3ba),\n            Emojicon.fromCodePoint(0x1f3b7),\n            Emojicon.fromCodePoint(0x1f3b8),\n            Emojicon.fromCodePoint(0x1f47e),\n            Emojicon.fromCodePoint(0x1f3ae),\n            Emojicon.fromCodePoint(0x1f0cf),\n            Emojicon.fromCodePoint(0x1f3b4),\n            Emojicon.fromCodePoint(0x1f004),\n            Emojicon.fromCodePoint(0x1f3b2),\n            Emojicon.fromCodePoint(0x1f3af),\n            Emojicon.fromCodePoint(0x1f3c8),\n            Emojicon.fromCodePoint(0x1f3c0),\n            Emojicon.fromChar((char) 0x26bd),\n            Emojicon.fromChar((char) 0x26be),\n            Emojicon.fromCodePoint(0x1f3be),\n            Emojicon.fromCodePoint(0x1f3b1),\n            Emojicon.fromCodePoint(0x1f3c9),\n            Emojicon.fromCodePoint(0x1f3b3),\n            Emojicon.fromChar((char) 0x26f3),\n            Emojicon.fromCodePoint(0x1f6b5),\n            Emojicon.fromCodePoint(0x1f6b4),\n            Emojicon.fromCodePoint(0x1f3c1),\n            Emojicon.fromCodePoint(0x1f3c7),\n            Emojicon.fromCodePoint(0x1f3c6),\n            Emojicon.fromCodePoint(0x1f3bf),\n            Emojicon.fromCodePoint(0x1f3c2),\n            Emojicon.fromCodePoint(0x1f3ca),\n            Emojicon.fromCodePoint(0x1f3c4),\n            Emojicon.fromCodePoint(0x1f3a3),\n            Emojicon.fromChar((char) 0x2615),\n            Emojicon.fromCodePoint(0x1f375),\n            Emojicon.fromCodePoint(0x1f376),\n            Emojicon.fromCodePoint(0x1f37c),\n            Emojicon.fromCodePoint(0x1f37a),\n            Emojicon.fromCodePoint(0x1f37b),\n            Emojicon.fromCodePoint(0x1f378),\n            Emojicon.fromCodePoint(0x1f379),\n            Emojicon.fromCodePoint(0x1f377),\n            Emojicon.fromCodePoint(0x1f374),\n            Emojicon.fromCodePoint(0x1f355),\n            Emojicon.fromCodePoint(0x1f354),\n            Emojicon.fromCodePoint(0x1f35f),\n            Emojicon.fromCodePoint(0x1f357),\n            Emojicon.fromCodePoint(0x1f356),\n            Emojicon.fromCodePoint(0x1f35d),\n            Emojicon.fromCodePoint(0x1f35b),\n            Emojicon.fromCodePoint(0x1f364),\n            Emojicon.fromCodePoint(0x1f371),\n            Emojicon.fromCodePoint(0x1f363),\n            Emojicon.fromCodePoint(0x1f365),\n            Emojicon.fromCodePoint(0x1f359),\n            Emojicon.fromCodePoint(0x1f358),\n            Emojicon.fromCodePoint(0x1f35a),\n            Emojicon.fromCodePoint(0x1f35c),\n            Emojicon.fromCodePoint(0x1f372),\n            Emojicon.fromCodePoint(0x1f362),\n            Emojicon.fromCodePoint(0x1f361),\n            Emojicon.fromCodePoint(0x1f373),\n            Emojicon.fromCodePoint(0x1f35e),\n            Emojicon.fromCodePoint(0x1f369),\n            Emojicon.fromCodePoint(0x1f36e),\n            Emojicon.fromCodePoint(0x1f366),\n            Emojicon.fromCodePoint(0x1f368),\n            Emojicon.fromCodePoint(0x1f367),\n            Emojicon.fromCodePoint(0x1f382),\n            Emojicon.fromCodePoint(0x1f370),\n            Emojicon.fromCodePoint(0x1f36a),\n            Emojicon.fromCodePoint(0x1f36b),\n            Emojicon.fromCodePoint(0x1f36c),\n            Emojicon.fromCodePoint(0x1f36d),\n            Emojicon.fromCodePoint(0x1f36f),\n            Emojicon.fromCodePoint(0x1f34e),\n            Emojicon.fromCodePoint(0x1f34f),\n            Emojicon.fromCodePoint(0x1f34a),\n            Emojicon.fromCodePoint(0x1f34b),\n            Emojicon.fromCodePoint(0x1f352),\n            Emojicon.fromCodePoint(0x1f347),\n            Emojicon.fromCodePoint(0x1f349),\n            Emojicon.fromCodePoint(0x1f353),\n            Emojicon.fromCodePoint(0x1f351),\n            Emojicon.fromCodePoint(0x1f348),\n            Emojicon.fromCodePoint(0x1f34c),\n            Emojicon.fromCodePoint(0x1f350),\n            Emojicon.fromCodePoint(0x1f34d),\n            Emojicon.fromCodePoint(0x1f360),\n            Emojicon.fromCodePoint(0x1f346),\n            Emojicon.fromCodePoint(0x1f345),\n            Emojicon.fromCodePoint(0x1f33d),\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/People.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\n\npublic class People {\n    public static final Emojicon[] DATA = new Emojicon[]{\n            Emojicon.fromCodePoint(0x1f604),\n            Emojicon.fromCodePoint(0x1f603),\n            Emojicon.fromCodePoint(0x1f600),\n            Emojicon.fromCodePoint(0x1f60a),\n            Emojicon.fromChar((char) 0x263a),\n            Emojicon.fromCodePoint(0x1f609),\n            Emojicon.fromCodePoint(0x1f60d),\n            Emojicon.fromCodePoint(0x1f618),\n            Emojicon.fromCodePoint(0x1f61a),\n            Emojicon.fromCodePoint(0x1f617),\n            Emojicon.fromCodePoint(0x1f619),\n            Emojicon.fromCodePoint(0x1f61c),\n            Emojicon.fromCodePoint(0x1f61d),\n            Emojicon.fromCodePoint(0x1f61b),\n            Emojicon.fromCodePoint(0x1f633),\n            Emojicon.fromCodePoint(0x1f601),\n            Emojicon.fromCodePoint(0x1f614),\n            Emojicon.fromCodePoint(0x1f60c),\n            Emojicon.fromCodePoint(0x1f612),\n            Emojicon.fromCodePoint(0x1f61e),\n            Emojicon.fromCodePoint(0x1f623),\n            Emojicon.fromCodePoint(0x1f622),\n            Emojicon.fromCodePoint(0x1f602),\n            Emojicon.fromCodePoint(0x1f62d),\n            Emojicon.fromCodePoint(0x1f62a),\n            Emojicon.fromCodePoint(0x1f625),\n            Emojicon.fromCodePoint(0x1f630),\n            Emojicon.fromCodePoint(0x1f605),\n            Emojicon.fromCodePoint(0x1f613),\n            Emojicon.fromCodePoint(0x1f629),\n            Emojicon.fromCodePoint(0x1f62b),\n            Emojicon.fromCodePoint(0x1f628),\n            Emojicon.fromCodePoint(0x1f631),\n            Emojicon.fromCodePoint(0x1f620),\n            Emojicon.fromCodePoint(0x1f621),\n            Emojicon.fromCodePoint(0x1f624),\n            Emojicon.fromCodePoint(0x1f616),\n            Emojicon.fromCodePoint(0x1f606),\n            Emojicon.fromCodePoint(0x1f60b),\n            Emojicon.fromCodePoint(0x1f637),\n            Emojicon.fromCodePoint(0x1f60e),\n            Emojicon.fromCodePoint(0x1f634),\n            Emojicon.fromCodePoint(0x1f635),\n            Emojicon.fromCodePoint(0x1f632),\n            Emojicon.fromCodePoint(0x1f61f),\n            Emojicon.fromCodePoint(0x1f626),\n            Emojicon.fromCodePoint(0x1f627),\n            Emojicon.fromCodePoint(0x1f608),\n            Emojicon.fromCodePoint(0x1f47f),\n            Emojicon.fromCodePoint(0x1f62e),\n            Emojicon.fromCodePoint(0x1f62c),\n            Emojicon.fromCodePoint(0x1f610),\n            Emojicon.fromCodePoint(0x1f615),\n            Emojicon.fromCodePoint(0x1f62f),\n            Emojicon.fromCodePoint(0x1f636),\n            Emojicon.fromCodePoint(0x1f607),\n            Emojicon.fromCodePoint(0x1f60f),\n            Emojicon.fromCodePoint(0x1f611),\n            Emojicon.fromCodePoint(0x1f472),\n            Emojicon.fromCodePoint(0x1f473),\n            Emojicon.fromCodePoint(0x1f46e),\n            Emojicon.fromCodePoint(0x1f477),\n            Emojicon.fromCodePoint(0x1f482),\n            Emojicon.fromCodePoint(0x1f476),\n            Emojicon.fromCodePoint(0x1f466),\n            Emojicon.fromCodePoint(0x1f467),\n            Emojicon.fromCodePoint(0x1f468),\n            Emojicon.fromCodePoint(0x1f469),\n            Emojicon.fromCodePoint(0x1f474),\n            Emojicon.fromCodePoint(0x1f475),\n            Emojicon.fromCodePoint(0x1f471),\n            Emojicon.fromCodePoint(0x1f47c),\n            Emojicon.fromCodePoint(0x1f478),\n            Emojicon.fromCodePoint(0x1f63a),\n            Emojicon.fromCodePoint(0x1f638),\n            Emojicon.fromCodePoint(0x1f63b),\n            Emojicon.fromCodePoint(0x1f63d),\n            Emojicon.fromCodePoint(0x1f63c),\n            Emojicon.fromCodePoint(0x1f640),\n            Emojicon.fromCodePoint(0x1f63f),\n            Emojicon.fromCodePoint(0x1f639),\n            Emojicon.fromCodePoint(0x1f63e),\n            Emojicon.fromCodePoint(0x1f479),\n            Emojicon.fromCodePoint(0x1f47a),\n            Emojicon.fromCodePoint(0x1f648),\n            Emojicon.fromCodePoint(0x1f649),\n            Emojicon.fromCodePoint(0x1f64a),\n            Emojicon.fromCodePoint(0x1f480),\n            Emojicon.fromCodePoint(0x1f47d),\n            Emojicon.fromCodePoint(0x1f4a9),\n            Emojicon.fromCodePoint(0x1f525),\n            Emojicon.fromChar((char) 0x2728),\n            Emojicon.fromCodePoint(0x1f31f),\n            Emojicon.fromCodePoint(0x1f4ab),\n            Emojicon.fromCodePoint(0x1f4a5),\n            Emojicon.fromCodePoint(0x1f4a2),\n            Emojicon.fromCodePoint(0x1f4a6),\n            Emojicon.fromCodePoint(0x1f4a7),\n            Emojicon.fromCodePoint(0x1f4a4),\n            Emojicon.fromCodePoint(0x1f4a8),\n            Emojicon.fromCodePoint(0x1f442),\n            Emojicon.fromCodePoint(0x1f440),\n            Emojicon.fromCodePoint(0x1f443),\n            Emojicon.fromCodePoint(0x1f445),\n            Emojicon.fromCodePoint(0x1f444),\n            Emojicon.fromCodePoint(0x1f44d),\n            Emojicon.fromCodePoint(0x1f44e),\n            Emojicon.fromCodePoint(0x1f44c),\n            Emojicon.fromCodePoint(0x1f44a),\n            Emojicon.fromChar((char) 0x270a),\n            Emojicon.fromChar((char) 0x270c),\n            Emojicon.fromCodePoint(0x1f44b),\n            Emojicon.fromChar((char) 0x270b),\n            Emojicon.fromCodePoint(0x1f450),\n            Emojicon.fromCodePoint(0x1f446),\n            Emojicon.fromCodePoint(0x1f447),\n            Emojicon.fromCodePoint(0x1f449),\n            Emojicon.fromCodePoint(0x1f448),\n            Emojicon.fromCodePoint(0x1f64c),\n            Emojicon.fromCodePoint(0x1f64f),\n            Emojicon.fromChar((char) 0x261d),\n            Emojicon.fromCodePoint(0x1f44f),\n            Emojicon.fromCodePoint(0x1f4aa),\n            Emojicon.fromCodePoint(0x1f6b6),\n            Emojicon.fromCodePoint(0x1f3c3),\n            Emojicon.fromCodePoint(0x1f483),\n            Emojicon.fromCodePoint(0x1f46b),\n            Emojicon.fromCodePoint(0x1f46a),\n            Emojicon.fromCodePoint(0x1f46c),\n            Emojicon.fromCodePoint(0x1f46d),\n            Emojicon.fromCodePoint(0x1f48f),\n            Emojicon.fromCodePoint(0x1f491),\n            Emojicon.fromCodePoint(0x1f46f),\n            Emojicon.fromCodePoint(0x1f646),\n            Emojicon.fromCodePoint(0x1f645),\n            Emojicon.fromCodePoint(0x1f481),\n            Emojicon.fromCodePoint(0x1f64b),\n            Emojicon.fromCodePoint(0x1f486),\n            Emojicon.fromCodePoint(0x1f487),\n            Emojicon.fromCodePoint(0x1f485),\n            Emojicon.fromCodePoint(0x1f470),\n            Emojicon.fromCodePoint(0x1f64e),\n            Emojicon.fromCodePoint(0x1f64d),\n            Emojicon.fromCodePoint(0x1f647),\n            Emojicon.fromCodePoint(0x1f3a9),\n            Emojicon.fromCodePoint(0x1f451),\n            Emojicon.fromCodePoint(0x1f452),\n            Emojicon.fromCodePoint(0x1f45f),\n            Emojicon.fromCodePoint(0x1f45e),\n            Emojicon.fromCodePoint(0x1f461),\n            Emojicon.fromCodePoint(0x1f460),\n            Emojicon.fromCodePoint(0x1f462),\n            Emojicon.fromCodePoint(0x1f455),\n            Emojicon.fromCodePoint(0x1f454),\n            Emojicon.fromCodePoint(0x1f45a),\n            Emojicon.fromCodePoint(0x1f457),\n            Emojicon.fromCodePoint(0x1f3bd),\n            Emojicon.fromCodePoint(0x1f456),\n            Emojicon.fromCodePoint(0x1f458),\n            Emojicon.fromCodePoint(0x1f459),\n            Emojicon.fromCodePoint(0x1f4bc),\n            Emojicon.fromCodePoint(0x1f45c),\n            Emojicon.fromCodePoint(0x1f45d),\n            Emojicon.fromCodePoint(0x1f45b),\n            Emojicon.fromCodePoint(0x1f453),\n            Emojicon.fromCodePoint(0x1f380),\n            Emojicon.fromCodePoint(0x1f302),\n            Emojicon.fromCodePoint(0x1f484),\n            Emojicon.fromCodePoint(0x1f49b),\n            Emojicon.fromCodePoint(0x1f499),\n            Emojicon.fromCodePoint(0x1f49c),\n            Emojicon.fromCodePoint(0x1f49a),\n            Emojicon.fromChar((char) 0x2764),\n            Emojicon.fromCodePoint(0x1f494),\n            Emojicon.fromCodePoint(0x1f497),\n            Emojicon.fromCodePoint(0x1f493),\n            Emojicon.fromCodePoint(0x1f495),\n            Emojicon.fromCodePoint(0x1f496),\n            Emojicon.fromCodePoint(0x1f49e),\n            Emojicon.fromCodePoint(0x1f498),\n            Emojicon.fromCodePoint(0x1f48c),\n            Emojicon.fromCodePoint(0x1f48b),\n            Emojicon.fromCodePoint(0x1f48d),\n            Emojicon.fromCodePoint(0x1f48e),\n            Emojicon.fromCodePoint(0x1f464),\n            Emojicon.fromCodePoint(0x1f465),\n            Emojicon.fromCodePoint(0x1f4ac),\n            Emojicon.fromCodePoint(0x1f463),\n            Emojicon.fromCodePoint(0x1f4ad),\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/Places.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\n\npublic class Places {\n    public static final Emojicon[] DATA = new Emojicon[]{\n            Emojicon.fromCodePoint(0x1f3e0),\n            Emojicon.fromCodePoint(0x1f3e1),\n            Emojicon.fromCodePoint(0x1f3eb),\n            Emojicon.fromCodePoint(0x1f3e2),\n            Emojicon.fromCodePoint(0x1f3e3),\n            Emojicon.fromCodePoint(0x1f3e5),\n            Emojicon.fromCodePoint(0x1f3e6),\n            Emojicon.fromCodePoint(0x1f3ea),\n            Emojicon.fromCodePoint(0x1f3e9),\n            Emojicon.fromCodePoint(0x1f3e8),\n            Emojicon.fromCodePoint(0x1f492),\n            Emojicon.fromChar((char) 0x26ea),\n            Emojicon.fromCodePoint(0x1f3ec),\n            Emojicon.fromCodePoint(0x1f3e4),\n            Emojicon.fromCodePoint(0x1f307),\n            Emojicon.fromCodePoint(0x1f306),\n            Emojicon.fromCodePoint(0x1f3ef),\n            Emojicon.fromCodePoint(0x1f3f0),\n            Emojicon.fromChar((char) 0x26fa),\n            Emojicon.fromCodePoint(0x1f3ed),\n            Emojicon.fromCodePoint(0x1f5fc),\n            Emojicon.fromCodePoint(0x1f5fe),\n            Emojicon.fromCodePoint(0x1f5fb),\n            Emojicon.fromCodePoint(0x1f304),\n            Emojicon.fromCodePoint(0x1f305),\n            Emojicon.fromCodePoint(0x1f303),\n            Emojicon.fromCodePoint(0x1f5fd),\n            Emojicon.fromCodePoint(0x1f309),\n            Emojicon.fromCodePoint(0x1f3a0),\n            Emojicon.fromCodePoint(0x1f3a1),\n            Emojicon.fromChar((char) 0x26f2),\n            Emojicon.fromCodePoint(0x1f3a2),\n            Emojicon.fromCodePoint(0x1f6a2),\n            Emojicon.fromChar((char) 0x26f5),\n            Emojicon.fromCodePoint(0x1f6a4),\n            Emojicon.fromCodePoint(0x1f6a3),\n            Emojicon.fromChar((char) 0x2693),\n            Emojicon.fromCodePoint(0x1f680),\n            Emojicon.fromChar((char) 0x2708),\n            Emojicon.fromCodePoint(0x1f4ba),\n            Emojicon.fromCodePoint(0x1f681),\n            Emojicon.fromCodePoint(0x1f682),\n            Emojicon.fromCodePoint(0x1f68a),\n            Emojicon.fromCodePoint(0x1f689),\n            Emojicon.fromCodePoint(0x1f69e),\n            Emojicon.fromCodePoint(0x1f686),\n            Emojicon.fromCodePoint(0x1f684),\n            Emojicon.fromCodePoint(0x1f685),\n            Emojicon.fromCodePoint(0x1f688),\n            Emojicon.fromCodePoint(0x1f687),\n            Emojicon.fromCodePoint(0x1f69d),\n            Emojicon.fromCodePoint(0x1f68b),\n            Emojicon.fromCodePoint(0x1f683),\n            Emojicon.fromCodePoint(0x1f68e),\n            Emojicon.fromCodePoint(0x1f68c),\n            Emojicon.fromCodePoint(0x1f68d),\n            Emojicon.fromCodePoint(0x1f699),\n            Emojicon.fromCodePoint(0x1f698),\n            Emojicon.fromCodePoint(0x1f697),\n            Emojicon.fromCodePoint(0x1f695),\n            Emojicon.fromCodePoint(0x1f696),\n            Emojicon.fromCodePoint(0x1f69b),\n            Emojicon.fromCodePoint(0x1f69a),\n            Emojicon.fromCodePoint(0x1f6a8),\n            Emojicon.fromCodePoint(0x1f693),\n            Emojicon.fromCodePoint(0x1f694),\n            Emojicon.fromCodePoint(0x1f692),\n            Emojicon.fromCodePoint(0x1f691),\n            Emojicon.fromCodePoint(0x1f690),\n            Emojicon.fromCodePoint(0x1f6b2),\n            Emojicon.fromCodePoint(0x1f6a1),\n            Emojicon.fromCodePoint(0x1f69f),\n            Emojicon.fromCodePoint(0x1f6a0),\n            Emojicon.fromCodePoint(0x1f69c),\n            Emojicon.fromCodePoint(0x1f488),\n            Emojicon.fromCodePoint(0x1f68f),\n            Emojicon.fromCodePoint(0x1f3ab),\n            Emojicon.fromCodePoint(0x1f6a6),\n            Emojicon.fromCodePoint(0x1f6a5),\n            Emojicon.fromChar((char) 0x26a0),\n            Emojicon.fromCodePoint(0x1f6a7),\n            Emojicon.fromCodePoint(0x1f530),\n            Emojicon.fromChar((char) 0x26fd),\n            Emojicon.fromCodePoint(0x1f3ee),\n            Emojicon.fromCodePoint(0x1f3b0),\n            Emojicon.fromChar((char) 0x2668),\n            Emojicon.fromCodePoint(0x1f5ff),\n            Emojicon.fromCodePoint(0x1f3aa),\n            Emojicon.fromCodePoint(0x1f3ad),\n            Emojicon.fromCodePoint(0x1f4cd),\n            Emojicon.fromCodePoint(0x1f6a9),\n            Emojicon.fromChars(\"\\ud83c\\uddef\\ud83c\\uddf5\"),\n            Emojicon.fromChars(\"\\ud83c\\uddf0\\ud83c\\uddf7\"),\n            Emojicon.fromChars(\"\\ud83c\\udde9\\ud83c\\uddea\"),\n            Emojicon.fromChars(\"\\ud83c\\udde8\\ud83c\\uddf3\"),\n            Emojicon.fromChars(\"\\ud83c\\uddfa\\ud83c\\uddf8\"),\n            Emojicon.fromChars(\"\\ud83c\\uddeb\\ud83c\\uddf7\"),\n            Emojicon.fromChars(\"\\ud83c\\uddea\\ud83c\\uddf8\"),\n            Emojicon.fromChars(\"\\ud83c\\uddee\\ud83c\\uddf9\"),\n            Emojicon.fromChars(\"\\ud83c\\uddf7\\ud83c\\uddfa\"),\n            Emojicon.fromChars(\"\\ud83c\\uddec\\ud83c\\udde7\"),\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/emojicon/emoji/Symbols.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.emoji;\n\n\npublic class Symbols {\n    public static final Emojicon[] DATA = new Emojicon[]{\n            Emojicon.fromChars(\"\\u0031\\u20e3\"),\n            Emojicon.fromChars(\"\\u0032\\u20e3\"),\n            Emojicon.fromChars(\"\\u0033\\u20e3\"),\n            Emojicon.fromChars(\"\\u0034\\u20e3\"),\n            Emojicon.fromChars(\"\\u0035\\u20e3\"),\n            Emojicon.fromChars(\"\\u0036\\u20e3\"),\n            Emojicon.fromChars(\"\\u0037\\u20e3\"),\n            Emojicon.fromChars(\"\\u0038\\u20e3\"),\n            Emojicon.fromChars(\"\\u0039\\u20e3\"),\n            Emojicon.fromChars(\"\\u0030\\u20e3\"),\n            Emojicon.fromCodePoint(0x1f51f),\n            Emojicon.fromCodePoint(0x1f522),\n            Emojicon.fromChars(\"\\u0023\\u20e3\"),\n            Emojicon.fromCodePoint(0x1f523),\n            Emojicon.fromChar((char) 0x2b06),\n            Emojicon.fromChar((char) 0x2b07),\n            Emojicon.fromChar((char) 0x2b05),\n            Emojicon.fromChar((char) 0x27a1),\n            Emojicon.fromCodePoint(0x1f520),\n            Emojicon.fromCodePoint(0x1f521),\n            Emojicon.fromCodePoint(0x1f524),\n            Emojicon.fromChar((char) 0x2197),\n            Emojicon.fromChar((char) 0x2196),\n            Emojicon.fromChar((char) 0x2198),\n            Emojicon.fromChar((char) 0x2199),\n            Emojicon.fromChar((char) 0x2194),\n            Emojicon.fromChar((char) 0x2195),\n            Emojicon.fromCodePoint(0x1f504),\n            Emojicon.fromChar((char) 0x25c0),\n            Emojicon.fromChar((char) 0x25b6),\n            Emojicon.fromCodePoint(0x1f53c),\n            Emojicon.fromCodePoint(0x1f53d),\n            Emojicon.fromChar((char) 0x21a9),\n            Emojicon.fromChar((char) 0x21aa),\n            Emojicon.fromChar((char) 0x2139),\n            Emojicon.fromChar((char) 0x23ea),\n            Emojicon.fromChar((char) 0x23e9),\n            Emojicon.fromChar((char) 0x23eb),\n            Emojicon.fromChar((char) 0x23ec),\n            Emojicon.fromChar((char) 0x2935),\n            Emojicon.fromChar((char) 0x2934),\n            Emojicon.fromCodePoint(0x1f197),\n            Emojicon.fromCodePoint(0x1f500),\n            Emojicon.fromCodePoint(0x1f501),\n            Emojicon.fromCodePoint(0x1f502),\n            Emojicon.fromCodePoint(0x1f195),\n            Emojicon.fromCodePoint(0x1f199),\n            Emojicon.fromCodePoint(0x1f192),\n            Emojicon.fromCodePoint(0x1f193),\n            Emojicon.fromCodePoint(0x1f196),\n            Emojicon.fromCodePoint(0x1f4f6),\n            Emojicon.fromCodePoint(0x1f3a6),\n            Emojicon.fromCodePoint(0x1f201),\n            Emojicon.fromCodePoint(0x1f22f),\n            Emojicon.fromCodePoint(0x1f233),\n            Emojicon.fromCodePoint(0x1f235),\n            Emojicon.fromCodePoint(0x1f234),\n            Emojicon.fromCodePoint(0x1f232),\n            Emojicon.fromCodePoint(0x1f250),\n            Emojicon.fromCodePoint(0x1f239),\n            Emojicon.fromCodePoint(0x1f23a),\n            Emojicon.fromCodePoint(0x1f236),\n            Emojicon.fromCodePoint(0x1f21a),\n            Emojicon.fromCodePoint(0x1f6bb),\n            Emojicon.fromCodePoint(0x1f6b9),\n            Emojicon.fromCodePoint(0x1f6ba),\n            Emojicon.fromCodePoint(0x1f6bc),\n            Emojicon.fromCodePoint(0x1f6be),\n            Emojicon.fromCodePoint(0x1f6b0),\n            Emojicon.fromCodePoint(0x1f6ae),\n            Emojicon.fromCodePoint(0x1f17f),\n            Emojicon.fromChar((char) 0x267f),\n            Emojicon.fromCodePoint(0x1f6ad),\n            Emojicon.fromCodePoint(0x1f237),\n            Emojicon.fromCodePoint(0x1f238),\n            Emojicon.fromCodePoint(0x1f202),\n            Emojicon.fromChar((char) 0x24c2),\n            Emojicon.fromCodePoint(0x1f6c2),\n            Emojicon.fromCodePoint(0x1f6c4),\n            Emojicon.fromCodePoint(0x1f6c5),\n            Emojicon.fromCodePoint(0x1f6c3),\n            Emojicon.fromCodePoint(0x1f251),\n            Emojicon.fromChar((char) 0x3299),\n            Emojicon.fromChar((char) 0x3297),\n            Emojicon.fromCodePoint(0x1f191),\n            Emojicon.fromCodePoint(0x1f198),\n            Emojicon.fromCodePoint(0x1f194),\n            Emojicon.fromCodePoint(0x1f6ab),\n            Emojicon.fromCodePoint(0x1f51e),\n            Emojicon.fromCodePoint(0x1f4f5),\n            Emojicon.fromCodePoint(0x1f6af),\n            Emojicon.fromCodePoint(0x1f6b1),\n            Emojicon.fromCodePoint(0x1f6b3),\n            Emojicon.fromCodePoint(0x1f6b7),\n            Emojicon.fromCodePoint(0x1f6b8),\n            Emojicon.fromChar((char) 0x26d4),\n            Emojicon.fromChar((char) 0x2733),\n            Emojicon.fromChar((char) 0x2747),\n            Emojicon.fromChar((char) 0x274e),\n            Emojicon.fromChar((char) 0x2705),\n            Emojicon.fromChar((char) 0x2734),\n            Emojicon.fromCodePoint(0x1f49f),\n            Emojicon.fromCodePoint(0x1f19a),\n            Emojicon.fromCodePoint(0x1f4f3),\n            Emojicon.fromCodePoint(0x1f4f4),\n            Emojicon.fromCodePoint(0x1f170),\n            Emojicon.fromCodePoint(0x1f171),\n            Emojicon.fromCodePoint(0x1f18e),\n            Emojicon.fromCodePoint(0x1f17e),\n            Emojicon.fromCodePoint(0x1f4a0),\n            Emojicon.fromChar((char) 0x27bf),\n            Emojicon.fromChar((char) 0x267b),\n            Emojicon.fromChar((char) 0x2648),\n            Emojicon.fromChar((char) 0x2649),\n            Emojicon.fromChar((char) 0x264a),\n            Emojicon.fromChar((char) 0x264b),\n            Emojicon.fromChar((char) 0x264c),\n            Emojicon.fromChar((char) 0x264d),\n            Emojicon.fromChar((char) 0x264e),\n            Emojicon.fromChar((char) 0x264f),\n            Emojicon.fromChar((char) 0x2650),\n            Emojicon.fromChar((char) 0x2651),\n            Emojicon.fromChar((char) 0x2652),\n            Emojicon.fromChar((char) 0x2653),\n            Emojicon.fromChar((char) 0x26ce),\n            Emojicon.fromCodePoint(0x1f52f),\n            Emojicon.fromCodePoint(0x1f3e7),\n            Emojicon.fromCodePoint(0x1f4b9),\n            Emojicon.fromCodePoint(0x1f4b2),\n            Emojicon.fromCodePoint(0x1f4b1),\n//            Emoji.fromChar((char)0x00a9),\n//            Emoji.fromChar((char)0x00ae),\n            Emojicon.fromChar((char) 0xe24e),\n            Emojicon.fromChar((char) 0xe24f),\n\n            Emojicon.fromChar((char) 0x2122),\n            Emojicon.fromChar((char) 0x274c),\n            Emojicon.fromChar((char) 0x203c),\n            Emojicon.fromChar((char) 0x2049),\n            Emojicon.fromChar((char) 0x2757),\n            Emojicon.fromChar((char) 0x2753),\n            Emojicon.fromChar((char) 0x2755),\n            Emojicon.fromChar((char) 0x2754),\n            Emojicon.fromChar((char) 0x2b55),\n            Emojicon.fromCodePoint(0x1f51d),\n            Emojicon.fromCodePoint(0x1f51a),\n            Emojicon.fromCodePoint(0x1f519),\n            Emojicon.fromCodePoint(0x1f51b),\n            Emojicon.fromCodePoint(0x1f51c),\n            Emojicon.fromCodePoint(0x1f503),\n            Emojicon.fromCodePoint(0x1f55b),\n            Emojicon.fromCodePoint(0x1f567),\n            Emojicon.fromCodePoint(0x1f550),\n            Emojicon.fromCodePoint(0x1f55c),\n            Emojicon.fromCodePoint(0x1f551),\n            Emojicon.fromCodePoint(0x1f55d),\n            Emojicon.fromCodePoint(0x1f552),\n            Emojicon.fromCodePoint(0x1f55e),\n            Emojicon.fromCodePoint(0x1f553),\n            Emojicon.fromCodePoint(0x1f55f),\n            Emojicon.fromCodePoint(0x1f554),\n            Emojicon.fromCodePoint(0x1f560),\n            Emojicon.fromCodePoint(0x1f555),\n            Emojicon.fromCodePoint(0x1f556),\n            Emojicon.fromCodePoint(0x1f557),\n            Emojicon.fromCodePoint(0x1f558),\n            Emojicon.fromCodePoint(0x1f559),\n            Emojicon.fromCodePoint(0x1f55a),\n            Emojicon.fromCodePoint(0x1f561),\n            Emojicon.fromCodePoint(0x1f562),\n            Emojicon.fromCodePoint(0x1f563),\n            Emojicon.fromCodePoint(0x1f564),\n            Emojicon.fromCodePoint(0x1f565),\n            Emojicon.fromCodePoint(0x1f566),\n            Emojicon.fromChar((char) 0x2716),\n            Emojicon.fromChar((char) 0x2795),\n            Emojicon.fromChar((char) 0x2796),\n            Emojicon.fromChar((char) 0x2797),\n            Emojicon.fromChar((char) 0x2660),\n            Emojicon.fromChar((char) 0x2665),\n            Emojicon.fromChar((char) 0x2663),\n            Emojicon.fromChar((char) 0x2666),\n            Emojicon.fromCodePoint(0x1f4ae),\n            Emojicon.fromCodePoint(0x1f4af),\n            Emojicon.fromChar((char) 0x2714),\n            Emojicon.fromChar((char) 0x2611),\n            Emojicon.fromCodePoint(0x1f518),\n            Emojicon.fromCodePoint(0x1f517),\n            Emojicon.fromChar((char) 0x27b0),\n            Emojicon.fromChar((char) 0x3030),\n            Emojicon.fromChar((char) 0x303d),\n            Emojicon.fromCodePoint(0x1f531),\n            Emojicon.fromChar((char) 0x25fc),\n            Emojicon.fromChar((char) 0x25fb),\n            Emojicon.fromChar((char) 0x25fe),\n            Emojicon.fromChar((char) 0x25fd),\n            Emojicon.fromChar((char) 0x25aa),\n            Emojicon.fromChar((char) 0x25ab),\n            Emojicon.fromCodePoint(0x1f53a),\n            Emojicon.fromCodePoint(0x1f532),\n            Emojicon.fromCodePoint(0x1f533),\n            Emojicon.fromChar((char) 0x26ab),\n            Emojicon.fromChar((char) 0x26aa),\n            Emojicon.fromCodePoint(0x1f534),\n            Emojicon.fromCodePoint(0x1f535),\n            Emojicon.fromCodePoint(0x1f53b),\n            Emojicon.fromChar((char) 0x2b1c),\n            Emojicon.fromChar((char) 0x2b1b),\n            Emojicon.fromCodePoint(0x1f536),\n            Emojicon.fromCodePoint(0x1f537),\n            Emojicon.fromCodePoint(0x1f538),\n            Emojicon.fromCodePoint(0x1f539),\n    };\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/pageView/QDEmojiconPagerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.pageView;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.core.view.ViewCompat;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmuidemo.fragment.components.qqface.emojicon.EmojiconTextView;\nimport com.qmuiteam.qmuidemo.R;\n\n/**\n * @author cginechen\n * @date 2017-06-08\n */\n\npublic class QDEmojiconPagerView extends QDQQFaceBasePagerView {\n    public QDEmojiconPagerView(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected View getView(int position, View convertView, ViewGroup parent) {\n        EmojiconTextView emojiconTextView;\n        if (convertView == null || !(convertView instanceof EmojiconTextView)) {\n            emojiconTextView = new EmojiconTextView(getContext());\n            emojiconTextView.setTextSize(14);\n            int padding = QMUIDisplayHelper.dp2px(getContext(), 16);\n            ViewCompat.setBackground(emojiconTextView, QMUIResHelper.getAttrDrawable(\n                    getContext(), R.attr.qmui_skin_support_s_list_item_bg_1));\n            emojiconTextView.setPadding(padding, padding, padding, padding);\n            emojiconTextView.setMaxLines(8);\n            emojiconTextView.setTextColor(Color.BLACK);\n            emojiconTextView.setMovementMethodDefault();\n            convertView = emojiconTextView;\n        } else {\n            emojiconTextView = (EmojiconTextView) convertView;\n        }\n        emojiconTextView.setText(getItem(position));\n        return convertView;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/pageView/QDQQFaceBasePagerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.pageView;\n\nimport android.content.Context;\nimport androidx.core.content.ContextCompat;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.LinearLayout;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.link.QMUIScrollingMovementMethod;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.fragment.components.qqface.QDQQFaceTestData;\n\n/**\n * @author cginechen\n * @date 2017-06-08\n */\n\npublic abstract class QDQQFaceBasePagerView extends LinearLayout {\n    private TextView mLogTv;\n\n    private QDQQFaceTestData mTestData;\n\n    public QDQQFaceBasePagerView(Context context) {\n        super(context);\n\n        mTestData = new QDQQFaceTestData();\n\n        setOrientation(VERTICAL);\n        ListView listView = new ListView(context);\n        LinearLayout.LayoutParams listLp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);\n        listLp.weight = 1;\n        listView.setLayoutParams(listLp);\n        listView.setDivider(null);\n        listView.setDividerHeight(0);\n        listView.setAdapter(new MyAdapter());\n        addView(listView);\n\n        mLogTv = new TextView(context);\n        LinearLayout.LayoutParams logLp = new LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, QMUIDisplayHelper.dp2px(context, 60));\n        mLogTv.setLayoutParams(logLp);\n        mLogTv.setTextSize(12);\n        mLogTv.setBackgroundResource(R.drawable.qmui_divider_top_bitmap);\n        int paddingHor = QMUIDisplayHelper.dp2px(context, 16);\n        mLogTv.setPadding(paddingHor, 0, paddingHor, 0);\n        mLogTv.setTextColor(ContextCompat.getColor(context, R.color.qmui_config_color_black));\n        mLogTv.setMovementMethod(QMUIScrollingMovementMethod.getInstance());\n        addView(mLogTv);\n    }\n\n    protected CharSequence getItem(int position) {\n        return mTestData.getList().get(position);\n    }\n\n    private void refreshLogView(String msg) {\n        mLogTv.append(msg);\n        int offset = mLogTv.getLineCount() * mLogTv.getLineHeight();\n        if (offset > mLogTv.getHeight()) {\n            mLogTv.scrollTo(0, offset - mLogTv.getHeight());\n        }\n    }\n\n    protected abstract View getView(int position, View convertView, ViewGroup parent);\n\n    class MyAdapter extends BaseAdapter {\n\n        @Override\n        public int getCount() {\n            return mTestData.getList().size();\n        }\n\n        @Override\n        public CharSequence getItem(int position) {\n            return mTestData.getList().get(position);\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return 0;\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            long start = System.currentTimeMillis();\n            convertView = QDQQFaceBasePagerView.this.getView(position, convertView, parent);\n            long end = System.currentTimeMillis();\n            refreshLogView(\"getView : position = \" + position + \"; expend time = \" + (end - start) + \" \\n\");\n            return convertView;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/qqface/pageView/QDQQFacePagerView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.qqface.pageView;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.core.view.ViewCompat;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.qqface.QMUIQQFaceView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmuidemo.R;\n\n/**\n * @author cginechen\n * @date 2017-06-08\n */\n\npublic class QDQQFacePagerView extends QDQQFaceBasePagerView {\n    public QDQQFacePagerView(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected View getView(int position, View convertView, ViewGroup parent) {\n        QMUIQQFaceView qmuiqqFaceView;\n        if (convertView == null || !(convertView instanceof QMUIQQFaceView)) {\n            qmuiqqFaceView = new QMUIQQFaceView(getContext());\n            int padding = QMUIDisplayHelper.dp2px(getContext(), 16);\n            ViewCompat.setBackground(qmuiqqFaceView, QMUIResHelper.getAttrDrawable(\n                    getContext(), R.attr.qmui_skin_support_s_list_item_bg_1));\n            qmuiqqFaceView.setPadding(padding, padding, padding, padding);\n            qmuiqqFaceView.setLineSpace(QMUIDisplayHelper.dp2px(getContext(), 10));\n            qmuiqqFaceView.setTextColor(Color.BLACK);\n            qmuiqqFaceView.setMaxLine(8);\n            convertView = qmuiqqFaceView;\n        } else {\n            qmuiqqFaceView = (QMUIQQFaceView) convertView;\n        }\n        qmuiqqFaceView.setText(getItem(position));\n        return convertView;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDBaseSectionLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.recyclerView.QMUIRVDraggableScrollBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout;\nimport com.qmuiteam.qmui.widget.section.QMUISection;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionAdapter;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\n\nimport java.util.ArrayList;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\npublic abstract class QDBaseSectionLayoutFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_to_refresh)\n    QMUIPullRefreshLayout mPullRefreshLayout;\n    @BindView(R.id.section_layout)\n    QMUIStickySectionLayout mSectionLayout;\n\n    private RecyclerView.LayoutManager mLayoutManager;\n    protected QMUIStickySectionAdapter<SectionHeader, SectionItem, QMUIStickySectionAdapter.ViewHolder> mAdapter;\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_section_layout, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n        initRefreshLayout();\n        initStickyLayout();\n        initData();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button)\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        showBottomSheet();\n                    }\n                });\n    }\n\n    private void initRefreshLayout() {\n        mPullRefreshLayout.setOnPullListener(new QMUIPullRefreshLayout.OnPullListener() {\n            @Override\n            public void onMoveTarget(int offset) {\n\n            }\n\n            @Override\n            public void onMoveRefreshView(int offset) {\n\n            }\n\n            @Override\n            public void onRefresh() {\n                mPullRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mPullRefreshLayout.finishRefresh();\n                    }\n                }, 2000);\n            }\n        });\n    }\n\n    protected void initStickyLayout() {\n        mLayoutManager = createLayoutManager();\n        mSectionLayout.setLayoutManager(mLayoutManager);\n        QMUIRVDraggableScrollBar scrollBar = new QMUIRVDraggableScrollBar(0, 0, 0);\n        scrollBar.setEnableScrollBarFadeInOut(false);\n        scrollBar.attachToStickSectionLayout(mSectionLayout);\n    }\n\n    private void initData() {\n        mAdapter = createAdapter();\n        mAdapter.setCallback(new QMUIStickySectionAdapter.Callback<SectionHeader, SectionItem>() {\n            @Override\n            public void loadMore(final QMUISection<SectionHeader, SectionItem> section, final boolean loadMoreBefore) {\n                mSectionLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (isAttachedToActivity()) {\n                            ArrayList<SectionItem> list = new ArrayList<>();\n                            for (int i = 0; i < 10; i++) {\n                                list.add(new SectionItem(\"load more item \" + i));\n                            }\n                            mAdapter.finishLoadMore(section, list, loadMoreBefore, false);\n                        }\n                    }\n                }, 1000);\n            }\n\n            @Override\n            public void onItemClick(QMUIStickySectionAdapter.ViewHolder holder, int position) {\n                Toast.makeText(getContext(), \"click item \" + position, Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public boolean onItemLongClick(QMUIStickySectionAdapter.ViewHolder holder, int position) {\n                Toast.makeText(getContext(), \"long click item \" + position, Toast.LENGTH_SHORT).show();\n                return true;\n            }\n        });\n        mSectionLayout.setAdapter(mAdapter, true);\n        ArrayList<QMUISection<SectionHeader, SectionItem>> list = new ArrayList<>();\n        for (int i = 0; i < 10; i++) {\n            list.add(createSection(\"header \" + i, i%2 != 0));\n        }\n        mAdapter.setData(list);\n    }\n\n    private QMUISection<SectionHeader, SectionItem> createSection(String headerText, boolean isFold) {\n        SectionHeader header = new SectionHeader(headerText);\n        ArrayList<SectionItem> contents = new ArrayList<>();\n        for (int i = 0; i < 20; i++) {\n            contents.add(new SectionItem(\"item \" + i));\n        }\n        QMUISection<SectionHeader, SectionItem> section = new QMUISection<>(header, contents, isFold);\n        // if test load more, you can open the code\n        section.setExistAfterDataToLoad(true);\n//        section.setExistBeforeDataToLoad(true);\n        return section;\n    }\n\n    protected abstract QMUIStickySectionAdapter<\n            SectionHeader, SectionItem, QMUIStickySectionAdapter.ViewHolder> createAdapter();\n\n    protected abstract RecyclerView.LayoutManager createLayoutManager();\n\n\n    private void showBottomSheet() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getContext())\n                .addItem(\"test scroll to section header\")\n                .addItem(\"test scroll to section item\")\n                .addItem(\"test find position\")\n                .addItem(\"test find custom position\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        switch (position) {\n                            case 0: {\n                                QMUISection<SectionHeader, SectionItem> section = mAdapter.getSectionDirectly(3);\n                                if (section != null) {\n                                    mAdapter.scrollToSectionHeader(section, true);\n                                }\n                                break;\n                            }\n                            case 1: {\n                                QMUISection<SectionHeader, SectionItem> section = mAdapter.getSectionDirectly(3);\n                                if (section != null) {\n                                    SectionItem item = section.getItemAt(10);\n                                    if (item != null) {\n                                        mAdapter.scrollToSectionItem(section, item, true);\n                                    }\n                                }\n                                break;\n                            }\n                            case 2: {\n                                int targetPosition = mAdapter.findPosition(new QMUIStickySectionAdapter.PositionFinder<SectionHeader, SectionItem>() {\n                                    @Override\n                                    public boolean find(@NonNull QMUISection<SectionHeader, SectionItem> section, @Nullable SectionItem item) {\n                                        return \"header 4\".equals(section.getHeader().getText()) && (item != null && \"item 13\".equals(item.getText()));\n                                    }\n                                }, true);\n                                if (targetPosition != RecyclerView.NO_POSITION) {\n                                    Toast.makeText(getContext(), \"find position: \" + targetPosition, Toast.LENGTH_SHORT).show();\n                                    QMUISection<SectionHeader, SectionItem> section = mAdapter.getSection(targetPosition);\n                                    SectionItem item = mAdapter.getSectionItem(targetPosition);\n                                    if (item != null) {\n                                        mAdapter.scrollToSectionItem(section, item, true);\n                                    } else if (section != null) {\n                                        mAdapter.scrollToSectionHeader(section, true);\n                                    } else {\n                                        mLayoutManager.scrollToPosition(targetPosition);\n                                    }\n\n                                } else {\n                                    Toast.makeText(getContext(), \"failed to find position\", Toast.LENGTH_SHORT).show();\n                                }\n                                break;\n                            }\n                            case 3: {\n                                int targetPosition = mAdapter.findCustomPosition(QMUISection.SECTION_INDEX_UNKNOWN, QDListWithDecorationSectionAdapter.ITEM_INDEX_LIST_FOOTER, false);\n                                if (targetPosition != RecyclerView.NO_POSITION) {\n                                    Toast.makeText(getContext(), \"find position: \" + targetPosition, Toast.LENGTH_SHORT).show();\n                                    mLayoutManager.scrollToPosition(targetPosition);\n                                }\n                            }\n                        }\n                        dialog.dismiss();\n                    }\n                })\n                .build().show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDGridSectionAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.section.QMUIDefaultStickySectionAdapter;\nimport com.qmuiteam.qmui.widget.section.QMUISection;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\nimport com.qmuiteam.qmuidemo.view.QDLoadingItemView;\nimport com.qmuiteam.qmuidemo.view.QDSectionHeaderView;\n\npublic class QDGridSectionAdapter extends QMUIDefaultStickySectionAdapter<SectionHeader, SectionItem> {\n\n    public QDGridSectionAdapter() {\n    }\n\n    public QDGridSectionAdapter(boolean removeSectionTitleIfOnlyOneSection) {\n        super(removeSectionTitleIfOnlyOneSection);\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateSectionHeaderViewHolder(@NonNull ViewGroup viewGroup) {\n        return new ViewHolder(new QDSectionHeaderView(viewGroup.getContext()));\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateSectionItemViewHolder(@NonNull ViewGroup viewGroup) {\n        Context context = viewGroup.getContext();\n        int paddingHor = QMUIDisplayHelper.dp2px(context, 24);\n        int paddingVer = QMUIDisplayHelper.dp2px(context, 16);\n        TextView tv = new TextView(context);\n        tv.setTextSize(14);\n        tv.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_gray_9));\n        tv.setTextColor(Color.DKGRAY);\n        tv.setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n        tv.setGravity(Gravity.CENTER);\n        return new ViewHolder(tv);\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateSectionLoadingViewHolder(@NonNull ViewGroup viewGroup) {\n        return new ViewHolder(new QDLoadingItemView(viewGroup.getContext()));\n    }\n\n    @Override\n    protected void onBindSectionHeader(final ViewHolder holder, final int position, QMUISection<SectionHeader, SectionItem> section) {\n        QDSectionHeaderView itemView = (QDSectionHeaderView) holder.itemView;\n        itemView.render(section.getHeader(), section.isFold());\n        itemView.getArrowView().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                int pos = holder.isForStickyHeader ? position : holder.getAdapterPosition();\n                toggleFold(pos, false);\n            }\n        });\n    }\n\n    @Override\n    protected void onBindSectionItem(ViewHolder holder, int position, QMUISection<SectionHeader, SectionItem> section, int itemIndex) {\n        ((TextView) holder.itemView).setText(section.getItemAt(itemIndex).getText());\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDGridSectionLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport android.graphics.Rect;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionAdapter;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\n\n@Widget(group = Group.Other, name = \"Sticky Section for Grid\")\npublic class QDGridSectionLayoutFragment extends QDBaseSectionLayoutFragment {\n    @Override\n    protected QMUIStickySectionAdapter<SectionHeader, SectionItem, QMUIStickySectionAdapter.ViewHolder> createAdapter() {\n        return new QDGridSectionAdapter();\n    }\n\n    @Override\n    protected RecyclerView.LayoutManager createLayoutManager() {\n        final GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 3);\n        layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {\n            @Override\n            public int getSpanSize(int i) {\n                return mAdapter.getItemIndex(i) < 0 ? layoutManager.getSpanCount() : 1;\n            }\n        });\n        return layoutManager;\n    }\n\n    @Override\n    protected void initStickyLayout() {\n        super.initStickyLayout();\n        mSectionLayout.getRecyclerView().addItemDecoration(new RecyclerView.ItemDecoration() {\n            @Override\n            public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n                if(view instanceof TextView){\n                    int margin = QMUIDisplayHelper.dp2px(getContext(), 10);\n                    outRect.set(margin, margin, margin, margin);\n                }else{\n                    outRect.set(0, 0, 0, 0);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDListSectionAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.R;\n\npublic class QDListSectionAdapter extends QDGridSectionAdapter {\n\n    public QDListSectionAdapter() {\n    }\n\n    public QDListSectionAdapter(boolean removeSectionTitleIfOnlyOneSection) {\n        super(removeSectionTitleIfOnlyOneSection);\n    }\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateSectionItemViewHolder(@NonNull ViewGroup viewGroup) {\n        Context context = viewGroup.getContext();\n        int paddingHor = QMUIDisplayHelper.dp2px(context, 24);\n        int paddingVer = QMUIDisplayHelper.dp2px(context, 16);\n        TextView tv = new TextView(context);\n        tv.setTextSize(14);\n        tv.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_gray_9));\n        tv.setTextColor(Color.DKGRAY);\n        tv.setPadding(paddingHor, paddingVer, paddingHor, paddingVer);\n        return new ViewHolder(tv);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDListSectionLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionAdapter;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\n\n@Widget(group = Group.Other, name = \"Sticky Section for List\")\npublic class QDListSectionLayoutFragment extends QDBaseSectionLayoutFragment {\n\n    @Override\n    protected QMUIStickySectionAdapter<SectionHeader, SectionItem, QMUIStickySectionAdapter.ViewHolder> createAdapter() {\n        return new QDListSectionAdapter(true);\n    }\n\n    @Override\n    protected RecyclerView.LayoutManager createLayoutManager() {\n        return new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(\n                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDListWithDecorationSectionAdapter.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.section.QMUISection;\nimport com.qmuiteam.qmui.widget.section.QMUISectionDiffCallback;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\n\nimport java.util.List;\n\npublic class QDListWithDecorationSectionAdapter extends QDListSectionAdapter {\n\n    public static final int ITEM_INDEX_LIST_HEADER = -1;\n    public static final int ITEM_INDEX_LIST_FOOTER = -2;\n    public static final int ITEM_INDEX_SECTION_TIP_START = -3;\n    public static final int ITEM_INDEX_SECTION_TIP_END = -4;\n\n    public static final int ITEM_TYPE_LIST_HEADER = 1;\n    public static final int ITEM_TYPE_LIST_FOOTER = 2;\n    public static final int ITEM_TYPE_SECTION_TIP_START = 3;\n    public static final int ITEM_TYPE_SECTION_TIP_END = 4;\n\n\n    @NonNull\n    @Override\n    protected ViewHolder onCreateCustomItemViewHolder(@NonNull ViewGroup viewGroup, int type) {\n        View view;\n        Context context = viewGroup.getContext();\n        if (type == ITEM_TYPE_LIST_HEADER) {\n            ImageView iv = new ImageView(context);\n            iv.setImageResource(R.mipmap.example_image2);\n            view = iv;\n        } else if (type == ITEM_TYPE_LIST_FOOTER) {\n            TextView tv = new TextView(context);\n            tv.setTextSize(12);\n            tv.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_gray_9));\n            tv.setTextColor(Color.DKGRAY);\n            tv.setText(R.string.sticky_section_decoration_list_footer);\n            tv.setGravity(Gravity.CENTER);\n            int paddingVer = QMUIDisplayHelper.dp2px(context, 16);\n            tv.setPadding(0, paddingVer, 0, paddingVer);\n            view = tv;\n        } else if (type == ITEM_TYPE_SECTION_TIP_START) {\n            TextView tv = new TextView(context);\n            tv.setTextSize(12);\n            tv.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_gray_9));\n            tv.setTextColor(Color.DKGRAY);\n            tv.setText(R.string.sticky_section_decoration_section_top_tip);\n            tv.setGravity(Gravity.CENTER);\n            int paddingVer = QMUIDisplayHelper.dp2px(context, 16);\n            tv.setPadding(0, paddingVer, 0, paddingVer);\n            view = tv;\n        } else if (type == ITEM_TYPE_SECTION_TIP_END) {\n            TextView tv = new TextView(context);\n            tv.setTextSize(12);\n            tv.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_gray_9));\n            tv.setTextColor(Color.DKGRAY);\n            tv.setText(R.string.sticky_section_decoration_section_bottom_tip);\n            tv.setGravity(Gravity.CENTER);\n            int paddingVer = QMUIDisplayHelper.dp2px(context, 16);\n            tv.setPadding(0, paddingVer, 0, paddingVer);\n            view = tv;\n        } else {\n            view = new View(viewGroup.getContext());\n        }\n        return new ViewHolder(view);\n    }\n\n    @Override\n    protected int getCustomItemViewType(int itemIndex, int position) {\n        if (itemIndex == ITEM_INDEX_LIST_HEADER) {\n            return ITEM_TYPE_LIST_HEADER;\n        } else if (itemIndex == ITEM_INDEX_LIST_FOOTER) {\n            return ITEM_TYPE_LIST_FOOTER;\n        } else if (itemIndex == ITEM_INDEX_SECTION_TIP_START) {\n            return ITEM_TYPE_SECTION_TIP_START;\n        } else if (itemIndex == ITEM_INDEX_SECTION_TIP_END) {\n            return ITEM_TYPE_SECTION_TIP_END;\n        }\n        return super.getCustomItemViewType(itemIndex, position);\n    }\n\n    @Override\n    protected QMUISectionDiffCallback<SectionHeader, SectionItem> createDiffCallback(\n            List<QMUISection<SectionHeader, SectionItem>> lastData,\n            List<QMUISection<SectionHeader, SectionItem>> currentData) {\n        return new QMUISectionDiffCallback<SectionHeader, SectionItem>(lastData, currentData) {\n\n            @Override\n            protected void onGenerateCustomIndexBeforeSectionList(IndexGenerationInfo generationInfo, List<QMUISection<SectionHeader, SectionItem>> list) {\n                generationInfo.appendWholeListCustomIndex(ITEM_INDEX_LIST_HEADER);\n            }\n\n            @Override\n            protected void onGenerateCustomIndexAfterSectionList(IndexGenerationInfo generationInfo, List<QMUISection<SectionHeader, SectionItem>> list) {\n                generationInfo.appendWholeListCustomIndex(ITEM_INDEX_LIST_FOOTER);\n            }\n\n            @Override\n            protected void onGenerateCustomIndexBeforeItemList(IndexGenerationInfo generationInfo,\n                                                               QMUISection<SectionHeader, SectionItem> section,\n                                                               int sectionIndex) {\n                if (!section.isExistBeforeDataToLoad()) {\n                    generationInfo.appendCustomIndex(sectionIndex, ITEM_INDEX_SECTION_TIP_START);\n                }\n            }\n\n            @Override\n            protected void onGenerateCustomIndexAfterItemList(IndexGenerationInfo generationInfo,\n                                                              QMUISection<SectionHeader, SectionItem> section,\n                                                              int sectionIndex) {\n                if (!section.isExistAfterDataToLoad()) {\n                    generationInfo.appendCustomIndex(sectionIndex, ITEM_INDEX_SECTION_TIP_END);\n                }\n            }\n\n            @Override\n            protected boolean areCustomContentsTheSame(@Nullable QMUISection<SectionHeader, SectionItem> oldSection, int oldItemIndex, @Nullable QMUISection<SectionHeader, SectionItem> newSection, int newItemIndex) {\n                return true;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDListWithDecorationSectionLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionAdapter;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\nimport com.qmuiteam.qmuidemo.model.SectionItem;\n\n@Widget(group = Group.Other, name = \"Sticky Section for List(With Decoration)\")\npublic class QDListWithDecorationSectionLayoutFragment extends QDBaseSectionLayoutFragment {\n\n    @Override\n    protected QMUIStickySectionAdapter<SectionHeader, SectionItem, QMUIStickySectionAdapter.ViewHolder> createAdapter() {\n        return new QDListWithDecorationSectionAdapter();\n    }\n\n    @Override\n    protected RecyclerView.LayoutManager createLayoutManager() {\n        return new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(\n                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/section/QDSectionLayoutFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.section;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmui.widget.section.QMUIStickySectionLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIStickySectionLayout.class,\n        iconRes = R.mipmap.icon_grid_sticky_section,\n        docUrl = \"https://github.com/Tencent/QMUI_Android/wiki/QMUIStickySectionLayout\")\npublic class QDSectionLayoutFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n\n        injectDocToTopBar(mTopBar);\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDListSectionLayoutFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDListSectionLayoutFragment fragment = new QDListSectionLayoutFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDGridSectionLayoutFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDGridSectionLayoutFragment fragment = new QDGridSectionLayoutFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDListWithDecorationSectionLayoutFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDListWithDecorationSectionLayoutFragment fragment = new QDListWithDecorationSectionLayoutFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeActionFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.pullLayout.QDPullHorizontalTestFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.pullLayout.QDPullRefreshAndLoadMoreTestFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.pullLayout.QDPullVerticalTestFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(widgetClass = QMUIRVItemSwipeAction.class, iconRes = R.mipmap.icon_grid_rv_item_swipe_action)\npublic class QDRVSwipeActionFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeMutiActionFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeMutiActionFragment fragment = new QDRVSwipeMutiActionFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeMutiActionOnlyIconFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeMutiActionOnlyIconFragment fragment = new QDRVSwipeMutiActionOnlyIconFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeMutiActionWithIconFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeMutiActionWithIconFragment fragment = new QDRVSwipeMutiActionWithIconFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeSingleDeleteActionFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeSingleDeleteActionFragment fragment = new QDRVSwipeSingleDeleteActionFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeDeleteWithNoActionFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeDeleteWithNoActionFragment fragment = new QDRVSwipeDeleteWithNoActionFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDRVSwipeUpDeleteFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDRVSwipeUpDeleteFragment fragment = new QDRVSwipeUpDeleteFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeDeleteWithNoActionFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeAction;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Left: Delete With No Action\")\npublic class QDRVSwipeDeleteWithNoActionFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_LEFT;\n            }\n\n            @Override\n            public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n                super.onClickAction(swipeAction, selected, action);\n                mAdapter.remove(selected.getAdapterPosition());\n                Toast.makeText(getContext(),\n                        \"你点击了第 \" + selected.getAdapterPosition() + \" 个 item 的\" + action.getText(),\n                        Toast.LENGTH_SHORT).show();\n                swipeAction.clear();\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeMutiActionFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.icu.util.ValueIterator;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVDraggableScrollBar;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Left: Muti Actions\")\n@LatestVisitRecord\npublic class QDRVSwipeMutiActionFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private Adapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_LEFT;\n            }\n\n            @Override\n            public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n                super.onClickAction(swipeAction, selected, action);\n                Toast.makeText(getContext(),\n                        \"你点击了第 \" + selected.getAdapterPosition() + \" 个 item 的\" + action.getText(),\n                        Toast.LENGTH_SHORT).show();\n                if(action == mAdapter.mDeleteAction){\n                    mAdapter.remove(selected.getAdapterPosition());\n                }else{\n                    swipeAction.clear();\n                }\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new Adapter(getContext());\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n\n    class Adapter extends RecyclerView.Adapter<QMUISwipeViewHolder>{\n\n        private List<String> mData = new ArrayList<>();\n\n        final QMUISwipeAction mDeleteAction;\n        final QMUISwipeAction mWriteReviewAction;\n\n        public Adapter(Context context){\n            QMUISwipeAction.ActionBuilder builder = new QMUISwipeAction.ActionBuilder()\n                    .textSize(QMUIDisplayHelper.sp2px(context, 14))\n                    .textColor(Color.WHITE)\n                    .paddingStartEnd(QMUIDisplayHelper.dp2px(getContext(), 14));\n\n            mDeleteAction = builder.text(\"删除\").backgroundColor(Color.RED).build();\n            mWriteReviewAction = builder.text(\"写想法\").backgroundColor(Color.BLUE).build();\n        }\n\n        public void setData(@Nullable List<String> list) {\n            mData.clear();\n            if(list != null){\n                mData.addAll(list);\n            }\n            notifyDataSetChanged();\n        }\n\n        public void remove(int pos){\n            mData.remove(pos);\n            notifyItemRemoved(pos);\n        }\n\n        public void add(int pos, String item) {\n            mData.add(pos, item);\n            notifyItemInserted(pos);\n        }\n\n        public void prepend(@NonNull List<String> items){\n            mData.addAll(0, items);\n            notifyDataSetChanged();\n        }\n\n        public void append(@NonNull List<String> items){\n            mData.addAll(items);\n            notifyDataSetChanged();\n        }\n\n        @NonNull\n        @Override\n        public QMUISwipeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple_list_item_1, parent, false);\n            final QMUISwipeViewHolder vh = new QMUISwipeViewHolder(view);\n            vh.addSwipeAction(mDeleteAction);\n            vh.addSwipeAction(mWriteReviewAction);\n            view.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Toast.makeText(getContext(),\n                            \"click position=\" + vh.getAdapterPosition(),\n                            Toast.LENGTH_SHORT).show();\n                }\n            });\n            return vh;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull QMUISwipeViewHolder holder, int position) {\n            TextView textView = holder.itemView.findViewById(R.id.text);\n            textView.setText(mData.get(position));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mData.size();\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeMutiActionOnlyIconFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Left: Muti Actions With Only Icon\")\npublic class QDRVSwipeMutiActionOnlyIconFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private Adapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_LEFT;\n            }\n\n            @Override\n            public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n                super.onClickAction(swipeAction, selected, action);\n                Toast.makeText(getContext(),\n                        \"你点击了第 \" + selected.getAdapterPosition() + \" 个 item 的\" + action.getText(),\n                        Toast.LENGTH_SHORT).show();\n                if(mAdapter.mAction1 == action){\n                    mAdapter.remove(selected.getAdapterPosition());\n                }else{\n                    swipeAction.clear();\n                }\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new Adapter(getContext());\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n\n    class Adapter extends RecyclerView.Adapter<QMUISwipeViewHolder>{\n\n        private List<String> mData = new ArrayList<>();\n\n        final QMUISwipeAction mAction1;\n        final QMUISwipeAction mAction2;\n\n        public Adapter(Context context){\n            QMUISwipeAction.ActionBuilder builder = new QMUISwipeAction.ActionBuilder()\n                    .textSize(QMUIDisplayHelper.sp2px(context, 14))\n                    .textColor(Color.WHITE)\n                    .paddingStartEnd(QMUIDisplayHelper.dp2px(getContext(), 14));\n\n            mAction1 = builder\n                    .backgroundColor(Color.RED)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_delete_line))\n                    .orientation(QMUISwipeAction.ActionBuilder.VERTICAL)\n                    .reverseDrawOrder(false)\n                    .build();\n            mAction2 = builder\n                    .backgroundColor(Color.BLUE)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_share))\n                    .orientation(QMUISwipeAction.ActionBuilder.VERTICAL)\n                    .reverseDrawOrder(true)\n                    .build();\n        }\n\n        public void setData(@Nullable List<String> list) {\n            mData.clear();\n            if(list != null){\n                mData.addAll(list);\n            }\n            notifyDataSetChanged();\n        }\n\n        public void remove(int pos){\n            mData.remove(pos);\n            notifyItemRemoved(pos);\n        }\n\n        public void add(int pos, String item) {\n            mData.add(pos, item);\n            notifyItemInserted(pos);\n        }\n\n        public void prepend(@NonNull List<String> items){\n            mData.addAll(0, items);\n            notifyDataSetChanged();\n        }\n\n        public void append(@NonNull List<String> items){\n            mData.addAll(items);\n            notifyDataSetChanged();\n        }\n\n        @NonNull\n        @Override\n        public QMUISwipeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple_list_item_1, parent, false);\n            final QMUISwipeViewHolder vh = new QMUISwipeViewHolder(view);\n            vh.addSwipeAction(mAction1);\n            vh.addSwipeAction(mAction2);\n            view.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Toast.makeText(getContext(),\n                            \"click position=\" + vh.getAdapterPosition(),\n                            Toast.LENGTH_SHORT).show();\n                }\n            });\n            return vh;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull QMUISwipeViewHolder holder, int position) {\n            TextView textView = holder.itemView.findViewById(R.id.text);\n            textView.setText(mData.get(position));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mData.size();\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeMutiActionWithIconFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Left: Muti Actions With Icon\")\npublic class QDRVSwipeMutiActionWithIconFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private Adapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_LEFT;\n            }\n\n            @Override\n            public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n                super.onClickAction(swipeAction, selected, action);\n                Toast.makeText(getContext(),\n                        \"你点击了第 \" + selected.getAdapterPosition() + \" 个 item 的\" + action.getText(),\n                        Toast.LENGTH_SHORT).show();\n                if(mAdapter.mAction1 == action){\n                    mAdapter.remove(selected.getAdapterPosition());\n                }else{\n                    swipeAction.clear();\n                }\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new Adapter(getContext());\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n\n    class Adapter extends RecyclerView.Adapter<QMUISwipeViewHolder>{\n\n        private List<String> mData = new ArrayList<>();\n\n        final QMUISwipeAction mAction1;\n        final QMUISwipeAction mAction2;\n        final QMUISwipeAction mAction3;\n        final QMUISwipeAction mAction4;\n\n        public Adapter(Context context){\n            QMUISwipeAction.ActionBuilder builder = new QMUISwipeAction.ActionBuilder()\n                    .textSize(QMUIDisplayHelper.sp2px(context, 14))\n                    .textColor(Color.WHITE)\n                    .paddingStartEnd(QMUIDisplayHelper.dp2px(getContext(), 14));\n\n            mAction1 = builder\n                    .text(\"删除\")\n                    .backgroundColor(Color.RED)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_delete_line))\n                    .orientation(QMUISwipeAction.ActionBuilder.VERTICAL)\n                    .reverseDrawOrder(false)\n                    .build();\n            mAction2 = builder\n                    .text(\"查词典\")\n                    .backgroundColor(Color.BLUE)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_dict))\n                    .orientation(QMUISwipeAction.ActionBuilder.VERTICAL)\n                    .reverseDrawOrder(true)\n                    .build();\n            mAction3 = builder\n                    .text(\"分享\")\n                    .backgroundColor(Color.BLACK)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_share))\n                    .orientation(QMUISwipeAction.ActionBuilder.HORIZONTAL)\n                    .reverseDrawOrder(false)\n                    .build();\n            mAction4 = builder\n                    .text(\"复制\")\n                    .backgroundColor(Color.GRAY)\n                    .icon(ContextCompat.getDrawable(context, R.drawable.icon_quick_action_copy))\n                    .orientation(QMUISwipeAction.ActionBuilder.HORIZONTAL)\n                    .reverseDrawOrder(true)\n                    .build();\n        }\n\n        public void setData(@Nullable List<String> list) {\n            mData.clear();\n            if(list != null){\n                mData.addAll(list);\n            }\n            notifyDataSetChanged();\n        }\n\n        public void remove(int pos){\n            mData.remove(pos);\n            notifyItemRemoved(pos);\n        }\n\n        public void add(int pos, String item) {\n            mData.add(pos, item);\n            notifyItemInserted(pos);\n        }\n\n        public void prepend(@NonNull List<String> items){\n            mData.addAll(0, items);\n            notifyDataSetChanged();\n        }\n\n        public void append(@NonNull List<String> items){\n            mData.addAll(items);\n            notifyDataSetChanged();\n        }\n\n        @NonNull\n        @Override\n        public QMUISwipeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple_list_item_1, parent, false);\n            final QMUISwipeViewHolder vh = new QMUISwipeViewHolder(view);\n            vh.addSwipeAction(mAction1);\n            vh.addSwipeAction(mAction2);\n            vh.addSwipeAction(mAction3);\n            vh.addSwipeAction(mAction4);\n            view.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Toast.makeText(getContext(),\n                            \"click position=\" + vh.getAdapterPosition(),\n                            Toast.LENGTH_SHORT).show();\n                }\n            });\n            return vh;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull QMUISwipeViewHolder holder, int position) {\n            TextView textView = holder.itemView.findViewById(R.id.text);\n            textView.setText(mData.get(position));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mData.size();\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeSingleDeleteActionFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeAction;\nimport com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Left: Single Action And Allow Deletion\")\npublic class QDRVSwipeSingleDeleteActionFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private Adapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_refresh_and_load_more_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(@NonNull QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_TOP) {\n                            onRefreshData();\n                        } else if (pullAction.getPullEdge() == QMUIPullLayout.PULL_EDGE_BOTTOM) {\n                            onLoadMore();\n                        }\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 3000);\n            }\n        });\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.remove(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_LEFT;\n            }\n\n            @Override\n            public void onClickAction(QMUIRVItemSwipeAction swipeAction, RecyclerView.ViewHolder selected, QMUISwipeAction action) {\n                super.onClickAction(swipeAction, selected, action);\n                mAdapter.remove(selected.getAdapterPosition());\n            }\n        });\n        swipeAction.attachToRecyclerView(mRecyclerView);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new Adapter(getContext());\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\",\n                \"Metabolism\", \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    private void onRefreshData() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onRefreshData-\" + id + \"-\" + i);\n        }\n        mAdapter.prepend(data);\n        mRecyclerView.scrollToPosition(0);\n    }\n\n    private void onLoadMore() {\n        List<String> data = new ArrayList<>();\n        long id = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            data.add(\"onLoadMore-\" + id + \"-\" + i);\n        }\n        mAdapter.append(data);\n    }\n\n    class Adapter extends RecyclerView.Adapter<QMUISwipeViewHolder> {\n\n        private List<String> mData = new ArrayList<>();\n\n        private final QMUISwipeAction mDeleteAction;\n\n        public Adapter(Context context) {\n            QMUISwipeAction.ActionBuilder builder = new QMUISwipeAction.ActionBuilder()\n                    .textSize(QMUIDisplayHelper.sp2px(context, 14))\n                    .textColor(Color.WHITE)\n                    .paddingStartEnd(QMUIDisplayHelper.dp2px(getContext(), 14));\n\n            mDeleteAction = builder.text(\"删除\").backgroundColor(Color.RED).build();\n        }\n\n        public void setData(@Nullable List<String> list) {\n            mData.clear();\n            if (list != null) {\n                mData.addAll(list);\n            }\n            notifyDataSetChanged();\n        }\n\n        public void remove(int pos) {\n            mData.remove(pos);\n            notifyItemRemoved(pos);\n        }\n\n        public void add(int pos, String item) {\n            mData.add(pos, item);\n            notifyItemInserted(pos);\n        }\n\n        public void prepend(@NonNull List<String> items) {\n            mData.addAll(0, items);\n            notifyDataSetChanged();\n        }\n\n        public void append(@NonNull List<String> items) {\n            mData.addAll(items);\n            notifyDataSetChanged();\n        }\n\n        @NonNull\n        @Override\n        public QMUISwipeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple_list_item_1, parent, false);\n            final QMUISwipeViewHolder vh = new QMUISwipeViewHolder(view);\n            vh.addSwipeAction(mDeleteAction);\n            view.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Toast.makeText(getContext(),\n                            \"click position=\" + vh.getAdapterPosition(),\n                            Toast.LENGTH_SHORT).show();\n                }\n            });\n            return vh;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull QMUISwipeViewHolder holder, int position) {\n            TextView textView = holder.itemView.findViewById(R.id.text);\n            textView.setText(mData.get(position));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mData.size();\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/swipeAction/QDRVSwipeUpDeleteFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.swipeAction;\n\nimport android.app.Service;\nimport android.os.Vibrator;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.PagerSnapHelper;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.QMUIInterpolatorStaticHolder;\nimport com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Other, name = \"Swipe Up: Long Press To Swipe Delete\")\npublic class QDRVSwipeUpDeleteFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.pull_layout)\n    QMUIPullLayout mPullLayout;\n    @BindView(R.id.recyclerView)\n    RecyclerView mRecyclerView;\n    private QDRecyclerViewAdapter mAdapter;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pull_horizontal_test_layout, null);\n        ButterKnife.bind(this, root);\n\n        QDDataManager QDDataManager = com.qmuiteam.qmuidemo.manager.QDDataManager.getInstance();\n        mQDItemDescription = QDDataManager.getDescription(this.getClass());\n        initTopBar();\n        initData();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initData() {\n        mPullLayout.setActionListener(new QMUIPullLayout.ActionListener() {\n            @Override\n            public void onActionTriggered(QMUIPullLayout.PullAction pullAction) {\n                mPullLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mPullLayout.finishActionRun(pullAction);\n                    }\n                }, 1000);\n            }\n        });\n        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);\n        mRecyclerView.setLayoutManager(layoutManager);\n        new PagerSnapHelper().attachToRecyclerView(mRecyclerView);\n        mAdapter = new QDRecyclerViewAdapter();\n        mAdapter.setItemCount(10);\n        mRecyclerView.setAdapter(mAdapter);\n\n        QMUIRVItemSwipeAction swipeAction = new QMUIRVItemSwipeAction(true, new QMUIRVItemSwipeAction.Callback() {\n            @Override\n            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {\n                mAdapter.removeItem(viewHolder.getAdapterPosition());\n            }\n\n            @Override\n            public int getSwipeDirection(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n                return QMUIRVItemSwipeAction.SWIPE_UP;\n            }\n\n            @Override\n            public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {\n                return 0.3f;\n            }\n\n            @Override\n            public void onSelectedChanged(RecyclerView.ViewHolder selected) {\n                super.onSelectedChanged(selected);\n                if (selected != null) {\n                    mTopBar.setTitle(\"上滑删除\");\n                    selected.itemView.animate()\n                            .scaleX(1.02f)\n                            .scaleY(1.02f)\n                            .setInterpolator(QMUIInterpolatorStaticHolder.ACCELERATE_INTERPOLATOR)\n                            .setDuration(250)\n                            .start();\n\n                    // 震动\n                    Vibrator vibrator = (Vibrator) getContext().getSystemService(Service.VIBRATOR_SERVICE);\n                    vibrator.vibrate(10);\n                } else {\n                    mTopBar.setTitle(mQDItemDescription.getName());\n                }\n            }\n\n            @Override\n            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {\n                super.clearView(recyclerView, viewHolder);\n                View itemView = viewHolder.itemView;\n                if (itemView.getScaleX() != 1f || itemView.getScaleY() != 1f) {\n                    itemView.animate()\n                            .scaleX(1f)\n                            .scaleY(1f)\n                            .setInterpolator(QMUIInterpolatorStaticHolder.DECELERATE_INTERPOLATOR)\n                            .setDuration(250)\n                            .start();\n                } else {\n                    itemView.animate().cancel();\n                }\n            }\n        });\n        swipeAction.setPressTimeToSwipe(300);\n        swipeAction.attachToRecyclerView(mRecyclerView);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/viewpager/CardTransformer.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.viewpager;\n\nimport android.view.View;\n\nimport androidx.viewpager.widget.ViewPager;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\npublic class CardTransformer implements ViewPager.PageTransformer {\n    @Override\n    public void transformPage(View page, float position) {\n        // 刷新数据notifyDataSetChange之后也会调用到transformPage，但此时的position可能不在[-1, 1]之间\n        if (position <= -1 || position >= 1f) {\n            page.setRotation(0);\n        } else {\n            page.setRotation(position * 30);\n            page.setPivotX(page.getWidth() * .5f);\n            page.setPivotY(page.getHeight() * 1f);\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/viewpager/QDFitSystemWindowViewPagerFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.viewpager;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.QMUIFragmentPagerAdapter;\nimport com.qmuiteam.qmui.widget.QMUIViewPager;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDCollapsingTopBarLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDTabSegmentScrollableModeFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport androidx.annotation.Nullable;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\n@Widget(name = \"QDFitSystemWindowViewPagerFragment\")\npublic class QDFitSystemWindowViewPagerFragment extends BaseFragment {\n\n    @BindView(R.id.pager) QMUIViewPager mViewPager;\n    @BindView(R.id.tabs) QMUITabSegment mTabSegment;\n\n    @Override\n    protected View onCreateView() {\n        FrameLayout layout = (FrameLayout) LayoutInflater.from(getActivity()).inflate(R.layout.fragment_fsw_viewpager, null);\n        ButterKnife.bind(this, layout);\n        initPagers();\n        return layout;\n    }\n\n    private void initPagers() {\n        QMUIFragmentPagerAdapter pagerAdapter = new QMUIFragmentPagerAdapter(getChildFragmentManager()) {\n            @Override\n            public QMUIFragment createFragment(int position) {\n                switch (position) {\n                    case 0:\n                        return new QDTabSegmentScrollableModeFragment();\n                    case 1:\n                        return new QDCollapsingTopBarLayoutFragment();\n                    case 2:\n                        return new QDFitSystemWindowViewPagerFragment();\n                    case 3:\n                    default:\n                        return new QDViewPagerFragment();\n                }\n            }\n\n            @Override\n            public int getCount() {\n                return 4;\n            }\n\n            @Override\n            public CharSequence getPageTitle(int position) {\n                switch (position) {\n                    case 0:\n                        return \"TabSegment\";\n                    case 1:\n                        return \"CTopBar\";\n                    case 2:\n                        return \"IViewPager\";\n                    case 3:\n                    default:\n                        return \"ViewPager\";\n                }\n            }\n        };\n        mViewPager.setAdapter(pagerAdapter);\n        mTabSegment.setupWithViewPager(mViewPager);\n    }\n\n    @Override\n    protected boolean canDragBack() {\n        return mViewPager.getCurrentItem() == 0;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/viewpager/QDLoopViewPagerFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.viewpager;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUIPagerAdapter;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUIViewPager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\n@Widget(name = \"QDLoopViewPagerFragment\")\npublic class QDLoopViewPagerFragment extends BaseFragment {\n    @BindView(R.id.topbar) QMUITopBar mTopBar;\n    @BindView(R.id.pager) QMUIViewPager mViewPager;\n\n    private List<String> mItems = new ArrayList<>();\n\n\n    @Override\n    protected View onCreateView() {\n        FrameLayout layout = (FrameLayout) LayoutInflater.from(getActivity()).inflate(R.layout.fragment_loop_viewpager, null);\n        ButterKnife.bind(this, layout);\n        initData(5);\n        initTopBar();\n        initPagers();\n        return layout;\n    }\n\n    private void initData(int count) {\n        for (int i = 0; i < count; i++) {\n            mItems.add(String.valueOf(i));\n        }\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n    }\n\n\n    private void initPagers() {\n        QMUIPagerAdapter pagerAdapter = new QMUIPagerAdapter() {\n\n            @Override\n            public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {\n                return view == object;\n            }\n\n            @Override\n            public int getCount() {\n                return mItems.size();\n            }\n\n            @Override\n            public CharSequence getPageTitle(int position) {\n                return mItems.get(position);\n            }\n\n            @Override\n            @NonNull\n            protected Object hydrate(@NonNull ViewGroup container, int position) {\n                return new ItemView(getContext());\n            }\n\n            @Override\n            protected void populate(@NonNull ViewGroup container, @NonNull Object item, int position) {\n                ItemView itemView = (ItemView) item;\n                itemView.setText(mItems.get(position));\n                container.addView(itemView);\n            }\n\n            @Override\n            protected void destroy(@NonNull ViewGroup container, int position, @NonNull Object object) {\n                container.removeView((View) object);\n            }\n        };\n        //setPageTransformer默认采用ViewCompat.LAYER_TYPE_HARDWARE， 但它在某些4.x的国产机下会crash\n        boolean canUseHardware = Build.VERSION.SDK_INT >= 21;\n        mViewPager.setPageTransformer(false, new CardTransformer(),\n                canUseHardware ? ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_SOFTWARE);\n        mViewPager.setInfiniteRatio(500);\n        mViewPager.setEnableLoop(true);\n        mViewPager.setAdapter(pagerAdapter);\n    }\n\n    static class ItemView extends FrameLayout {\n        private TextView mTextView;\n\n        public ItemView(Context context) {\n            super(context);\n            mTextView = new TextView(context);\n            mTextView.setTextSize(20);\n            mTextView.setTextColor(ContextCompat.getColor(context, R.color.app_color_theme_5));\n            mTextView.setGravity(Gravity.CENTER);\n            mTextView.setBackgroundColor(ContextCompat.getColor(context, R.color.qmui_config_color_white));\n            int size = QMUIDisplayHelper.dp2px(context, 300);\n            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size, size);\n            lp.gravity = Gravity.CENTER;\n            addView(mTextView, lp);\n        }\n\n        public void setText(CharSequence text) {\n            mTextView.setText(text);\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/components/viewpager/QDViewPagerFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.components.viewpager;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.QMUIViewPager;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-09-13\n */\n\n@Widget(widgetClass = QMUIViewPager.class, iconRes = R.mipmap.icon_grid_pager_layout_manager)\npublic class QDViewPagerFragment extends BaseFragment {\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDFitSystemWindowViewPagerFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDFitSystemWindowViewPagerFragment fragment = new QDFitSystemWindowViewPagerFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDLoopViewPagerFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDLoopViewPagerFragment fragment = new QDLoopViewPagerFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/home/HomeComponentsController.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.home;\n\nimport android.content.Context;\n\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\n/**\n * @author cginechen\n * @date 2016-10-20\n */\n\npublic class HomeComponentsController extends HomeController {\n\n    public HomeComponentsController(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected String getTitle() {\n        return \"Components\";\n    }\n\n    @Override\n    protected ItemAdapter getItemAdapter() {\n        return new ItemAdapter(getContext(), QDDataManager.getInstance().getComponentsDescriptions());\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/home/HomeController.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.home;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Parcelable;\nimport android.util.SparseArray;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.decorator.GridDividerItemDecoration;\nimport com.qmuiteam.qmuidemo.fragment.QDAboutFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDNotchHelperFragment;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2016-10-20\n */\n\npublic abstract class HomeController extends LinearLayout {\n\n    protected QMUITopBarLayout mTopBar;\n    protected RecyclerView mRecyclerView;\n\n    private HomeControlListener mHomeControlListener;\n    private ItemAdapter mItemAdapter;\n    private int mDiffRecyclerViewSaveStateId = QMUIViewHelper.generateViewId();\n\n    public HomeController(Context context) {\n        super(context);\n        setOrientation(LinearLayout.VERTICAL);\n        mTopBar = new QMUITopBarLayout(context);\n        mTopBar.setId(View.generateViewId());\n        mTopBar.setFitsSystemWindows(true);\n        mRecyclerView = new RecyclerView(context);\n        mRecyclerView.setId(View.generateViewId());\n        addView(mTopBar, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        addView(mRecyclerView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,0, 1f));\n        initTopBar();\n        initRecyclerView();\n    }\n\n    protected void startFragment(BaseFragment fragment) {\n        if (mHomeControlListener != null) {\n            mHomeControlListener.startFragment(fragment);\n        }\n    }\n\n    public void setHomeControlListener(HomeControlListener homeControlListener) {\n        mHomeControlListener = homeControlListener;\n    }\n\n    protected abstract String getTitle();\n\n    private void initTopBar() {\n        mTopBar.setTitle(getTitle());\n\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_about, R.id.topbar_right_about_button).setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                QDAboutFragment fragment = new QDAboutFragment();\n                startFragment(fragment);\n            }\n        });\n    }\n\n    private void initRecyclerView() {\n        mItemAdapter = getItemAdapter();\n        mItemAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                QDItemDescription item = mItemAdapter.getItem(pos);\n                try {\n                    BaseFragment fragment = item.getDemoClass().newInstance();\n                    if (fragment instanceof QDNotchHelperFragment) {\n                        Context context = getContext();\n                        Intent intent = QDMainActivity.of(context, QDNotchHelperFragment.class);\n                        context.startActivity(intent);\n                        if (context instanceof Activity) {\n                            ((Activity) context).overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);\n                        }\n                    } else {\n                        startFragment(fragment);\n                    }\n\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        });\n        mRecyclerView.setAdapter(mItemAdapter);\n        int spanCount = 3;\n        mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), spanCount));\n        mRecyclerView.addItemDecoration(new GridDividerItemDecoration(getContext(), spanCount));\n    }\n\n    protected abstract ItemAdapter getItemAdapter();\n\n    public interface HomeControlListener {\n        void startFragment(BaseFragment fragment);\n    }\n\n    @Override\n    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {\n        int id = mRecyclerView.getId();\n        mRecyclerView.setId(mDiffRecyclerViewSaveStateId);\n        super.dispatchSaveInstanceState(container);\n        mRecyclerView.setId(id);\n    }\n\n    @Override\n    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {\n        int id = mRecyclerView.getId();\n        mRecyclerView.setId(mDiffRecyclerViewSaveStateId);\n        super.dispatchRestoreInstanceState(container);\n        mRecyclerView.setId(id);\n    }\n\n    static class ItemAdapter extends BaseRecyclerAdapter<QDItemDescription> {\n\n        public ItemAdapter(Context ctx, List<QDItemDescription> data) {\n            super(ctx, data);\n        }\n\n        @Override\n        public int getItemLayoutId(int viewType) {\n            return R.layout.home_item_layout;\n        }\n\n        @Override\n        public void bindData(RecyclerViewHolder holder, int position, QDItemDescription item) {\n            holder.getTextView(R.id.item_name).setText(item.getName());\n            if (item.getIconRes() != 0) {\n                holder.getImageView(R.id.item_icon).setImageResource(item.getIconRes());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/home/HomeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.home;\n\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\n\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentEffectHandler;\nimport com.qmuiteam.qmui.arch.effect.QMUIFragmentMapEffectHandler;\nimport com.qmuiteam.qmui.arch.effect.MapEffect;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.tab.QMUITab;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.model.CustomEffect;\n\nimport java.util.HashMap;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2016-10-19\n */\n\npublic class HomeFragment extends BaseFragment {\n    private final static String TAG = HomeFragment.class.getSimpleName();\n\n    @BindView(R.id.pager)\n    ViewPager mViewPager;\n    @BindView(R.id.tabs)\n    QMUITabSegment mTabSegment;\n    private HashMap<Pager, HomeController> mPages;\n    private PagerAdapter mPagerAdapter = new PagerAdapter() {\n\n        private int mChildCount = 0;\n\n        @Override\n        public boolean isViewFromObject(View view, Object object) {\n            return view == object;\n        }\n\n        @Override\n        public int getCount() {\n            return mPages.size();\n        }\n\n        @Override\n        public Object instantiateItem(final ViewGroup container, int position) {\n            HomeController page = mPages.get(Pager.getPagerFromPosition(position));\n            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            container.addView(page, params);\n            return page;\n        }\n\n        @Override\n        public void destroyItem(ViewGroup container, int position, Object object) {\n            container.removeView((View) object);\n        }\n\n        @Override\n        public int getItemPosition(Object object) {\n            if (mChildCount == 0) {\n                return POSITION_NONE;\n            }\n            return super.getItemPosition(object);\n        }\n\n        @Override\n        public void notifyDataSetChanged() {\n            mChildCount = getCount();\n            super.notifyDataSetChanged();\n        }\n    };\n\n    @Override\n    public void onAttach(@NonNull Context context) {\n        super.onAttach(context);\n        registerEffect(this, new QMUIFragmentMapEffectHandler() {\n            @Override\n            public boolean shouldHandleEffect(@NonNull MapEffect effect) {\n                return effect.getValue(\"interested_type_key\") != null;\n            }\n\n            @Override\n            public void handleEffect(@NonNull MapEffect effect) {\n                Object value = effect.getValue(\"interested_value_key\");\n                if(value instanceof String){\n                    Toast.makeText(context, ((String)value), Toast.LENGTH_SHORT).show();\n                }\n            }\n        });\n\n        registerEffect(this, new QMUIFragmentEffectHandler<CustomEffect>() {\n            @Override\n            public boolean shouldHandleEffect(@NonNull CustomEffect effect) {\n                return true;\n            }\n\n            @Override\n            public void handleEffect(@NonNull CustomEffect effect) {\n                Toast.makeText(context, effect.getContent(), Toast.LENGTH_SHORT).show();\n            }\n\n            @Override\n            public void handleEffect(@NonNull List<CustomEffect> effects) {\n               // we can only handle the last effect.\n               handleEffect(effects.get(effects.size() - 1));\n            }\n        });\n    }\n\n    @Override\n    protected View onCreateView() {\n        FrameLayout layout = (FrameLayout) LayoutInflater.from(getActivity()).inflate(R.layout.fragment_home, null);\n        ButterKnife.bind(this, layout);\n        initTabs();\n        initPagers();\n        return layout;\n    }\n\n\n    private void initTabs() {\n\n        QMUITabBuilder builder = mTabSegment.tabBuilder();\n        builder.setTypeface(null, Typeface.DEFAULT_BOLD);\n        builder.setSelectedIconScale(1.2f)\n                .setTextSize(QMUIDisplayHelper.sp2px(getContext(), 13), QMUIDisplayHelper.sp2px(getContext(), 15))\n                .setDynamicChangeIconColor(false);\n        QMUITab component = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component_selected))\n                .setText(\"Components\")\n                .build(getContext());\n        QMUITab util = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_util))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_util_selected))\n                .setText(\"Helper\")\n                .build(getContext());\n        QMUITab lab = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_lab))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_lab_selected))\n                .setText(\"Lab\")\n                .build(getContext());\n\n        mTabSegment.addTab(component)\n                .addTab(util)\n                .addTab(lab);\n    }\n\n    private void initPagers() {\n\n        HomeController.HomeControlListener listener = new HomeController.HomeControlListener() {\n            @Override\n            public void startFragment(BaseFragment fragment) {\n                HomeFragment.this.startFragment(fragment);\n            }\n        };\n\n        mPages = new HashMap<>();\n\n        HomeController homeComponentsController = new HomeComponentsController(getActivity());\n        homeComponentsController.setHomeControlListener(listener);\n        mPages.put(Pager.COMPONENT, homeComponentsController);\n\n        HomeController homeUtilController = new HomeUtilController(getActivity());\n        homeUtilController.setHomeControlListener(listener);\n        mPages.put(Pager.UTIL, homeUtilController);\n\n        HomeController homeLabController = new HomeLabController(getActivity());\n        homeLabController.setHomeControlListener(listener);\n        mPages.put(Pager.LAB, homeLabController);\n\n        mViewPager.setAdapter(mPagerAdapter);\n        mTabSegment.setupWithViewPager(mViewPager, false);\n    }\n\n    enum Pager {\n        COMPONENT, UTIL, LAB;\n\n        public static Pager getPagerFromPosition(int position) {\n            switch (position) {\n                case 0:\n                    return COMPONENT;\n                case 1:\n                    return UTIL;\n                case 2:\n                    return LAB;\n                default:\n                    return COMPONENT;\n            }\n        }\n    }\n\n    @Override\n    protected boolean canDragBack() {\n        return false;\n    }\n\n    @Override\n    public Object onLastFragmentFinish() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/home/HomeLabController.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.home;\n\nimport android.content.Context;\n\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\n/**\n * @author cginechen\n * @date 2016-10-20\n */\n\npublic class HomeLabController extends HomeController {\n\n    public HomeLabController(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected String getTitle() {\n        return \"Lab\";\n    }\n\n    @Override\n    protected ItemAdapter getItemAdapter() {\n        return new ItemAdapter(getContext(), QDDataManager.getInstance().getLabDescriptions());\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/home/HomeUtilController.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.home;\n\nimport android.content.Context;\n\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\n/** 主界面，关于 QMUI Util 部分的展示。\n * Created by Kayo on 2016/11/21.\n */\n\npublic class HomeUtilController extends HomeController {\n\n    public HomeUtilController(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected String getTitle() {\n        return \"Helper\";\n    }\n\n    @Override\n    protected ItemAdapter getItemAdapter() {\n        return new ItemAdapter(getContext(), QDDataManager.getInstance().getUtilDescriptions());\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDAnimationListViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUIAnimationListView;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.adaptor.QDSimpleAdapter;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-03-30\n */\n\n@Widget(group = Group.Lab, widgetClass = QMUIAnimationListView.class, iconRes = R.mipmap.icon_grid_anim_list_view)\npublic class QDAnimationListViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.listview) QMUIAnimationListView mListView;\n\n    private List<String> mData = new ArrayList<>();\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getContext()).inflate(R.layout.fragment_animation_listview, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n        initListView();\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n        mTopBar.addRightTextButton(\"添加\", QMUIViewHelper.generateViewId()).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mListView.manipulate(new QMUIAnimationListView.Manipulator<MyAdapter>() {\n                    @Override\n                    public void manipulate(MyAdapter adapter) {\n                        int position = mListView.getFirstVisiblePosition();\n                        long current = System.currentTimeMillis();\n                        mData.add(position + 1, \"item add\" + (current + 1));\n                        mData.add(position + 2, \"item add\" + (current + 2));\n                        mData.add(position + 3, \"item add\" + (current + 3));\n                    }\n                });\n\n            }\n        });\n        mTopBar.addRightTextButton(\"删除\", QMUIViewHelper.generateViewId()).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mListView.manipulate(new QMUIAnimationListView.Manipulator<MyAdapter>() {\n                    @Override\n                    public void manipulate(MyAdapter adapter) {\n                        int position = mListView.getFirstVisiblePosition();\n                        if(mData.size() > position + 4){\n                            mData.remove(position + 1);\n                            mData.remove(position + 3);\n                        }else{\n                            Toast.makeText(getContext(), \"item 已经很少了，不如先添加几个？\", Toast.LENGTH_SHORT).show();\n                        }\n\n                    }\n                });\n            }\n        });\n    }\n\n    private void initListView() {\n        for (int i = 0; i < 20; i++) {\n            mData.add(\"item \" + (i + 1));\n        }\n        MyAdapter adapter = new MyAdapter(getContext(), mData);\n        mListView.setAdapter(adapter);\n    }\n\n\n    private static class MyAdapter extends QDSimpleAdapter {\n        public MyAdapter(Context context, List<String> data) {\n            super(context, data);\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return getItem(position).hashCode();\n        }\n\n        @Override\n        public boolean hasStableIds() {\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDArchNavFragment.java",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentContainerView;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.QMUINavFragment;\nimport com.qmuiteam.qmui.arch.SwipeBackLayout;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.fragment.home.HomeFragment;\n\npublic class QDArchNavFragment extends QMUINavFragment {\n    private static final String TAG = \"QDArchNavFragment\";\n\n    public static QMUINavFragment getInstance(Class<? extends QMUIFragment> firstClass, @Nullable Bundle bundle) {\n        QMUINavFragment navFragment = new QDArchNavFragment();\n        navFragment.setArguments(initArguments(firstClass, bundle));\n        return navFragment;\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Bundle bundle = getArguments();\n        Log.i(TAG, \"1\");\n        if(bundle != null){\n            String navTest = bundle.getString(\"nav_test\");\n            if(navTest != null){\n                Log.i(TAG, \"latestVisit: \" + navTest);\n            }\n        }\n    }\n\n    @Override\n    protected View onCreateView() {\n        FrameLayout root = new FrameLayout(getContext());\n        FragmentContainerView fragmentContainerView = new FragmentContainerView(getContext());\n        TextView tipView = new TextView(getContext());\n        tipView.setText(\"Nav\");\n        tipView.setBackgroundColor(Color.RED);\n        tipView.setTextColor(Color.WHITE);\n        root.addView(fragmentContainerView);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        lp.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;\n        root.addView(tipView, lp);\n        configFragmentContainerView(fragmentContainerView);\n        return root;\n    }\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n        editor.putString(\"nav_test\", \"nav_test\");\n    }\n\n    @Override\n    public Object onLastFragmentFinish() {\n        return new HomeFragment();\n    }\n\n    @Override\n    protected int backViewInitOffset(Context context, int dragDirection, int moveEdge) {\n        if (moveEdge == SwipeBackLayout.EDGE_TOP || moveEdge == SwipeBackLayout.EDGE_BOTTOM) {\n            return 0;\n        }\n        return QMUIDisplayHelper.dp2px(context, 100);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDArchSurfaceTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.opengl.GLSurfaceView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\n\nimport javax.microedition.khronos.egl.EGLConfig;\nimport javax.microedition.khronos.opengles.GL10;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\nimport static android.opengl.GLES10.glClearColor;\nimport static android.opengl.GLES20.glViewport;\n\n//TODO xiaomi 8 surfaceView can not move when swipe back. It's ok in pixel\npublic class QDArchSurfaceTestFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.container) FrameLayout mContainer;\n    private GLSurfaceView mSurfaceView;\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getContext())\n                .inflate(R.layout.fragment_surface_test, null);\n        ButterKnife.bind(this, view);\n        mSurfaceView = new GLSurfaceView(getContext());\n        mSurfaceView.setEGLContextClientVersion(2);\n        mSurfaceView.setRenderer(new GLSurfaceView.Renderer() {\n            @Override\n            public void onSurfaceCreated(GL10 gl, EGLConfig config) {\n                glClearColor(0, 0, 0, 0);\n            }\n\n            @Override\n            public void onSurfaceChanged(GL10 gl, int width, int height) {\n                glViewport(0, 0, width, height);\n            }\n\n            @Override\n            public void onDrawFrame(GL10 gl) {\n\n            }\n        });\n        mContainer.addView(mSurfaceView);\n        initTopBar();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n        mTopBar.setTitle(\"Test SurfaceView\");\n        QDArchTestFragment.injectEntrance(mTopBar);\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        mSurfaceView.onResume();\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        mSurfaceView.onPause();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDArchTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.qmuiteam.qmui.arch.QMUIFragment;\nimport com.qmuiteam.qmui.arch.QMUINavFragment;\nimport com.qmuiteam.qmui.arch.SwipeBackLayout;\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.activity.ArchTestActivity;\nimport com.qmuiteam.qmuidemo.activity.TestArchInViewPagerActivity;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n\n@Widget(name = \"QMUIFragment\", iconRes = R.mipmap.icon_grid_layout)\n@LatestVisitRecord\npublic class QDArchTestFragment extends BaseFragment {\n    private static final String TAG = \"QDArchTestFragment\";\n    private static final String ARG_INDEX = \"arg_index\";\n    private static final int REQUEST_CODE = 1;\n    private static final String DATA_TEST = \"data_test\";\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.title)\n    TextView mTitleTv;\n    @BindView(R.id.btn)\n    QMUIRoundButton mBtn;\n    @BindView(R.id.btn_1)\n    QMUIRoundButton mBtn1;\n    @BindView(R.id.btn_2)\n    QMUIRoundButton mBtn2;\n    @BindView(R.id.btn_3)\n    QMUIRoundButton mBtn3;\n\n    private Holder mHolder = new Holder();\n\n    @Override\n    protected View onCreateView() {\n        Bundle args = getArguments();\n        final int index = args == null ? 1 : args.getInt(ARG_INDEX);\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_arch_test, null);\n        ButterKnife.bind(this, view);\n        mHolder.mTestView = view.findViewById(R.id.test);\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n        injectEntrance(mTopBar);\n        mTopBar.setTitle(String.valueOf(index));\n        mTitleTv.setText(String.valueOf(index));\n        final int next = index + 1;\n        final boolean destroyCurrent = next % 3 == 0;\n        String btnText = destroyCurrent ? \"startFragmentAndDestroyCurrent\" : \"startFragment\";\n        mBtn.setText(btnText);\n        mBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUIFragment fragment = newInstance(next);\n                if (destroyCurrent) {\n                    startFragmentAndDestroyCurrent(fragment);\n                } else {\n                    startFragmentForResult(fragment, REQUEST_CODE);\n                }\n\n            }\n        });\n        mBtn1.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n                Intent intent = QDMainActivity.of(getContext(), QDArchTestFragment.class);\n                startActivity(intent);\n            }\n        });\n\n        mBtn2.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUINavFragment fragment = QDArchNavFragment.getInstance(QDArchTestFragment.class, null);\n                startFragment(fragment);\n            }\n        });\n\n        mBtn3.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if(getParentFragment() instanceof QMUIFragment){\n                    ((QMUIFragment)getParentFragment()).startFragment(newInstance(next));\n                }\n            }\n        });\n        return view;\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Intent intent = new Intent();\n        intent.putExtra(DATA_TEST, \"test\");\n        setFragmentResult(RESULT_OK, intent);\n        Bundle arguments = getArguments();\n        if (arguments != null && arguments.getLong(\"test_long\") == 100\n                && arguments.getLong(\"test_long1\") == 1000\n                && arguments.getLong(\"test_long2\") == 400\n                && arguments.getLong(\"test_long3\", 200) == 200\n                && arguments.getFloat(\"test_float\") == 100.13f\n                && \"你好\".equals(arguments.getString(\"test_string\"))) {\n            Toast.makeText(getContext(), \"恢复到最近阅读(Muti)\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n        editor.putLong(\"test_long\", 100L);\n        editor.putLong(\"test_long1\", 1000);\n        editor.putLong(\"test_long2\", 400);\n        editor.putString(\"test_string\", \"你好\");\n        editor.putFloat(\"test_float\", 100.13f);\n    }\n\n    public static QDArchTestFragment newInstance(int index) {\n        Bundle args = new Bundle();\n        args.putInt(ARG_INDEX, index);\n        QDArchTestFragment fragment = new QDArchTestFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @Override\n    protected void onFragmentResult(int requestCode, int resultCode, Intent data) {\n        super.onFragmentResult(requestCode, resultCode, data);\n        if (data != null) {\n            Log.i(TAG, data.getStringExtra(DATA_TEST));\n        }\n    }\n\n    @Override\n    protected int getDragDirection(@NonNull SwipeBackLayout swipeBackLayout,\n                                   @NonNull SwipeBackLayout.ViewMoveAction viewMoveAction,\n                                   float downX, float downY, float dx, float dy, float slopTouch) {\n        if(dx >= slopTouch){\n            return SwipeBackLayout.DRAG_DIRECTION_LEFT_TO_RIGHT;\n        }else if(-dx>= slopTouch){\n            return SwipeBackLayout.DRAG_DIRECTION_RIGHT_TO_LEFT;\n        } else if(dy >= slopTouch){\n            return SwipeBackLayout.DRAG_DIRECTION_TOP_TO_BOTTOM;\n        }else if(-dy >= slopTouch){\n            return SwipeBackLayout.DRAG_DIRECTION_BOTTOM_TO_TOP;\n        }\n        return SwipeBackLayout.DRAG_DIRECTION_NONE;\n    }\n\n    public static void injectEntrance(final QMUITopBarLayout topbar) {\n        topbar.addRightTextButton(\"new Activity\", QMUIViewHelper.generateViewId())\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        showBottomSheetList(topbar.getContext());\n                    }\n                });\n    }\n\n    public static void showBottomSheetList(final Context context) {\n        new QMUIBottomSheet.BottomListSheetBuilder(context)\n                .addItem(\"Normal Arch Test\")\n                .addItem(\"WebView Test\")\n                .addItem(\"SurfaceView Test\")\n                .addItem(\"Directly Activity\")\n                .addItem(\"Directly Activity And Keep Bottom Sheet shown\")\n                .addItem(\"Show a Dialog\")\n                .addItem(\"QMUIFragment in QMUIActivity\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        if (position != 4) {\n                            dialog.dismiss();\n                        }\n\n                        if (position == 0) {\n                            Intent intent = QDMainActivity.of(context, QDArchTestFragment.class);\n                            context.startActivity(intent);\n                        } else if (position == 1) {\n                            Intent intent = QDMainActivity.createWebExplorerIntent(context,\n                                    \"https://github.com/Tencent/QMUI_Android\",\n                                    context.getResources().getString(R.string.about_item_github));\n                            context.startActivity(intent);\n                        } else if (position == 2) {\n                            Intent intent = QDMainActivity.of(context, QDArchSurfaceTestFragment.class);\n                            context.startActivity(intent);\n                        } else if (position == 3) {\n                            Intent intent = new Intent(context, ArchTestActivity.class);\n                            context.startActivity(intent);\n                        } else if (position == 4) {\n                            Intent intent = new Intent(context, ArchTestActivity.class);\n                            context.startActivity(intent);\n                        } else if (position == 5) {\n                            new QMUIDialog.MessageDialogBuilder(context)\n                                    .setMessage(\"click ok to go new activity. then swipe back, \" +\n                                            \"we should also see this dialog\")\n                                    .addAction(R.string.cancel, new QMUIDialogAction.ActionListener() {\n                                        @Override\n                                        public void onClick(QMUIDialog dialog, int index) {\n                                            dialog.dismiss();\n                                        }\n                                    })\n                                    .addAction(R.string.ok, new QMUIDialogAction.ActionListener() {\n                                        @Override\n                                        public void onClick(QMUIDialog dialog, int index) {\n                                            Intent intent = new Intent(context, ArchTestActivity.class);\n                                            context.startActivity(intent);\n                                        }\n                                    })\n                                    .show();\n                        } else if (position == 6) {\n                            Intent intent = new Intent(context, TestArchInViewPagerActivity.class);\n                            context.startActivity(intent);\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n\n    static class Holder {\n        TextView mTestView;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDArchWebViewTestFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment;\n\npublic class QDArchWebViewTestFragment extends QDWebExplorerFragment {\n\n    @Override\n    protected void initTopbar() {\n        super.initTopbar();\n        // for test\n//        QDArchTestFragment.injectEntrance(mTopBarLayout);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDComposeTipFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.style.TextOverflow\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.constraintlayout.compose.ChainStyle\nimport androidx.constraintlayout.compose.ConstraintLayout\nimport androidx.constraintlayout.compose.Dimension\nimport com.qmuiteam.compose.core.ui.QMUITopBarBackIconItem\nimport com.qmuiteam.compose.core.ui.QMUITopBarWithLazyScrollState\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.ComposeBaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\n\n@Widget(name = \"QMUI Compose Tip\", iconRes = R.mipmap.icon_grid_in_progress)\n@LatestVisitRecord\nclass QDComposeTipFragment : ComposeBaseFragment() {\n    @Composable\n    override fun PageContent() {\n        Column(modifier = Modifier.fillMaxSize()) {\n            val scrollState = rememberLazyListState()\n            QMUITopBarWithLazyScrollState(\n                scrollState = scrollState,\n                title = \"QMUIPhoto\",\n                leftItems = arrayListOf(\n                    QMUITopBarBackIconItem {\n                        popBackStack()\n                    }\n                )\n            )\n            LazyColumn(\n                state = scrollState,\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .weight(1f)\n                    .background(Color.White)\n            ) {\n                item {\n                    Column(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(12.dp)\n                    ) {\n                        Text(\n                            text = \"在 UI 开发过程中，经常会遇到如下一个需求：\\n\" +\n                                    \"假设一个布局是 【头像】【人名】【推荐信息】，正常用 LinearLayout 实现， \" +\n                                    \"是没有任何问题的，但是要求在人名过长，整体内容会超过容器宽度时，\" +\n                                    \"不要省略推荐信息，而是省略人名信息。\",\n                            fontSize = 13.sp,\n                            modifier = Modifier.padding(vertical = 12.dp)\n                        )\n                        ConstraintLayout(\n                            modifier = Modifier\n                                .fillMaxWidth()\n                                .height(100.dp)\n                                .background(Color.LightGray)\n                        ) {\n                            val (one, two, three, four) = createRefs()\n                            val horChain = createHorizontalChain(one, two, three, chainStyle = ChainStyle.Packed(0f))\n                            constrain(horChain) {\n                                start.linkTo(parent.start)\n                                end.linkTo(four.start)\n                            }\n                            Text(\n                                \"此处不压缩\",\n                                color = Color.White,\n                                maxLines = 1,\n                                modifier = Modifier\n                                    .background(Color.Red)\n                                    .constrainAs(one) {\n                                        top.linkTo(parent.top)\n                                        bottom.linkTo(parent.bottom)\n                                    })\n                            Text(\n                                \"此处如果内容有那么一点点过长，那就压缩省略压缩省略压缩省略\",\n                                color = Color.White,\n                                maxLines = 1,\n                                overflow = TextOverflow.Ellipsis,\n                                modifier = Modifier\n                                    .background(Color.Green)\n                                    .constrainAs(two) {\n                                        width = Dimension.preferredWrapContent\n                                        top.linkTo(parent.top)\n                                        bottom.linkTo(parent.bottom)\n                                    })\n                            Text(\n                                \"此处也不压缩\",\n                                color = Color.White,\n                                maxLines = 1,\n                                modifier = Modifier\n                                    .background(Color.Black)\n                                    .constrainAs(three) {\n                                        top.linkTo(parent.top)\n                                        bottom.linkTo(parent.bottom)\n                                    })\n                            Box(\n                                modifier = Modifier\n                                    .fillMaxHeight()\n                                    .width(50.dp)\n                                    .background(Color.Blue)\n                                    .constrainAs(four) {\n                                        top.linkTo(parent.top)\n                                        bottom.linkTo(parent.bottom)\n                                        end.linkTo(parent.end)\n                                    }\n                            )\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousBottomView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.IQMUIContinuousNestedBottomView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomDelegateLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUIPagerAdapter;\nimport com.qmuiteam.qmui.widget.QMUIViewPager;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic class QDContinuousBottomView extends QMUIContinuousNestedBottomDelegateLayout {\n\n    private MyViewPager mViewPager;\n    private QMUIContinuousNestedBottomRecyclerView mCurrentItemView;\n    private int mCurrentPosition = -1;\n    private IQMUIContinuousNestedBottomView.OnScrollNotifier mOnScrollNotifier;\n\n    public QDContinuousBottomView(Context context) {\n        super(context);\n    }\n\n    public QDContinuousBottomView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public QDContinuousBottomView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @NonNull\n    @Override\n    protected View onCreateHeaderView() {\n        TextView headerView = new TextView(getContext());\n        headerView.setTextSize(16);\n        headerView.setTextColor(Color.BLACK);\n        headerView.setBackgroundColor(Color.LTGRAY);\n        headerView.setGravity(Gravity.CENTER);\n        headerView.setText(\"This is normal view with ViewPager below\");\n        return headerView;\n    }\n\n    @Override\n    protected int getHeaderHeightLayoutParam() {\n        return QMUIDisplayHelper.dp2px(getContext(), 200);\n    }\n\n    @Override\n    protected int getHeaderStickyHeight() {\n        return QMUIDisplayHelper.dp2px(getContext(), 50);\n    }\n\n\n    @NonNull\n    @Override\n    protected View onCreateContentView() {\n        mViewPager = new MyViewPager(getContext());\n        mViewPager.setAdapter(new QMUIPagerAdapter() {\n            @Override\n            protected Object hydrate(ViewGroup container, int position) {\n                QMUIContinuousNestedBottomRecyclerView recyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n\n                recyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n                    @Override\n                    public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                                ViewGroup.LayoutParams.MATCH_PARENT);\n                    }\n                });\n\n                BaseRecyclerAdapter<String> adapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n                    @Override\n                    public int getItemLayoutId(int viewType) {\n                        return android.R.layout.simple_list_item_1;\n                    }\n\n                    @Override\n                    public void bindData(RecyclerViewHolder holder, int position, String item) {\n                        holder.setText(android.R.id.text1, item);\n                    }\n                };\n                adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n                    @Override\n                    public void onItemClick(View itemView, int pos) {\n                        Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n                    }\n                });\n                recyclerView.setAdapter(adapter);\n                onDataLoaded(adapter);\n                return recyclerView;\n            }\n\n            @Override\n            protected void populate(ViewGroup container, Object item, int position) {\n                container.addView((View) item);\n            }\n\n            @Override\n            protected void destroy(ViewGroup container, int position, Object object) {\n                container.removeView((View) object);\n            }\n\n            @Override\n            public int getCount() {\n                return 3;\n            }\n\n            @Override\n            public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {\n                return view == o;\n            }\n\n            @Override\n            public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {\n                super.setPrimaryItem(container, position, object);\n                mCurrentItemView = (QMUIContinuousNestedBottomRecyclerView) object;\n                mCurrentPosition = position;\n                if (mOnScrollNotifier != null) {\n                    mCurrentItemView.injectScrollNotifier(mOnScrollNotifier);\n                }\n            }\n        });\n        return mViewPager;\n    }\n\n    private void onDataLoaded(BaseRecyclerAdapter<String> adapter) {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\", \"Health\",\n                \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\", \"Bracket\",\n                \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\", \"Bolster\",\n                \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        adapter.setData(data);\n    }\n\n    class MyViewPager extends QMUIViewPager implements IQMUIContinuousNestedBottomView {\n        static final String KEY_CURRENT_POSITION = \"demo_bottom_current_position\";\n\n        public MyViewPager(Context context) {\n            super(context);\n        }\n\n        @Override\n        public void consumeScroll(int dyUnconsumed) {\n            if (mCurrentItemView != null) {\n                mCurrentItemView.consumeScroll(dyUnconsumed);\n            }\n\n        }\n\n        @Override\n        public void smoothScrollYBy(int dy, int duration) {\n            if (mCurrentItemView != null) {\n                mCurrentItemView.smoothScrollYBy(dy, duration);\n            }\n        }\n\n        @Override\n        public void stopScroll() {\n            if (mCurrentItemView != null) {\n                mCurrentItemView.stopScroll();\n            }\n        }\n\n        @Override\n        public int getContentHeight() {\n            if (mCurrentItemView != null) {\n                return mCurrentItemView.getContentHeight();\n            }\n            return 0;\n        }\n\n        @Override\n        public int getCurrentScroll() {\n            if (mCurrentItemView != null) {\n                return mCurrentItemView.getCurrentScroll();\n            }\n            return 0;\n        }\n\n        @Override\n        public int getScrollOffsetRange() {\n            if (mCurrentItemView != null) {\n                return mCurrentItemView.getScrollOffsetRange();\n            }\n            return getHeight();\n        }\n\n        @Override\n        public void injectScrollNotifier(OnScrollNotifier notifier) {\n            mOnScrollNotifier = notifier;\n            if (mCurrentItemView != null) {\n                mCurrentItemView.injectScrollNotifier(notifier);\n            }\n        }\n\n        @Override\n        public void saveScrollInfo(@NonNull Bundle bundle) {\n            bundle.putInt(KEY_CURRENT_POSITION, mCurrentPosition);\n            if(mCurrentItemView != null){\n                mCurrentItemView.saveScrollInfo(bundle);\n            }\n        }\n\n        @Override\n        public void restoreScrollInfo(@NonNull Bundle bundle) {\n            if(mCurrentItemView != null){\n                int currentPos = bundle.getInt(KEY_CURRENT_POSITION, -1);\n                if(currentPos == mCurrentPosition){\n                    mCurrentItemView.restoreScrollInfo(bundle);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll1Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.arch.record.RecordArgumentEditor;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopWebView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.annotation.Nullable;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"webview + recyclerview\")\n@LatestVisitRecord\npublic class QDContinuousNestedScroll1Fragment extends QDContinuousNestedScrollBaseFragment {\n\n    private QMUIWebView mNestedWebView;\n    private RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Bundle arguments = getArguments();\n        if (arguments != null && arguments.getInt(\"fragment_test\") == 20) {\n            Toast.makeText(getContext(), \"恢复到最近阅读(Int)\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mNestedWebView = new QMUIContinuousNestedTopWebView(getContext());\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams webViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        webViewLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mNestedWebView, webViewLp);\n\n        mRecyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mRecyclerView, recyclerViewLp);\n\n        mNestedWebView.loadUrl(\"https://mp.weixin.qq.com/s/zgfLOMD2JfZJKfHx-5BsBg\");\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\",\n                \"Nuturally\", \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\",\n                \"Apron\", \"Carpet\", \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    @Override\n    public void onCollectLatestVisitArgument(RecordArgumentEditor editor) {\n        editor.putInt(\"fragment_test\", 20);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mNestedWebView != null) {\n            mCoordinatorLayout.removeView(mNestedWebView);\n            mNestedWebView.destroy();\n            mNestedWebView = null;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll2Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.util.Log;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopWebView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\n@Widget(group = Group.Other, name = \"webview + part sticky header + viewpager\")\npublic class QDContinuousNestedScroll2Fragment extends QDContinuousNestedScrollBaseFragment {\n    private static final String TAG = \"ContinuousNestedScroll\";\n\n    private QMUIWebView mNestedWebView;\n    private QDContinuousBottomView mBottomView;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mNestedWebView = new QMUIContinuousNestedTopWebView(getContext());\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams webViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        webViewLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mNestedWebView, webViewLp);\n\n        mBottomView = new QDContinuousBottomView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mBottomView, recyclerViewLp);\n\n        mNestedWebView.loadUrl(\"https://mp.weixin.qq.com/s/zgfLOMD2JfZJKfHx-5BsBg\");\n\n        mCoordinatorLayout.addOnScrollListener(new QMUIContinuousNestedScrollLayout.OnScrollListener() {\n\n            @Override\n            public void onScroll(QMUIContinuousNestedScrollLayout scrollLayout, int topCurrent, int topRange, int offsetCurrent,\n                                 int offsetRange, int bottomCurrent, int bottomRange) {\n                Log.i(TAG, String.format(\"topCurrent = %d; topRange = %d; \" +\n                                \"offsetCurrent = %d; offsetRange = %d; \" +\n                                \"bottomCurrent = %d, bottomRange = %d\",\n                        topCurrent, topRange, offsetCurrent, offsetRange, bottomCurrent, bottomRange));\n            }\n\n            @Override\n            public void onScrollStateChange(QMUIContinuousNestedScrollLayout scrollLayout, int newScrollState, boolean fromTopBehavior) {\n\n            }\n        });\n    }\n\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mNestedWebView != null) {\n            mCoordinatorLayout.removeView(mNestedWebView);\n            mNestedWebView.destroy();\n            mNestedWebView = null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll3Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopRecyclerView;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"recyclerview + recyclerview\")\npublic class QDContinuousNestedScroll3Fragment extends QDContinuousNestedScrollBaseFragment {\n\n    private QMUIContinuousNestedTopRecyclerView mTopRecyclerView;\n    private RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopRecyclerView = new QMUIContinuousNestedTopRecyclerView(getContext());\n        mTopRecyclerView.setBackgroundColor(Color.LTGRAY);\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams webViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        webViewLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopRecyclerView, webViewLp);\n\n        mRecyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mRecyclerView, recyclerViewLp);\n\n        mTopRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mTopRecyclerView.setAdapter(mAdapter);\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\",\n                \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\",\n                \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll4Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopDelegateLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopRecyclerView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"(header + recyclerview + bottom) + recyclerview\")\npublic class QDContinuousNestedScroll4Fragment extends QDContinuousNestedScrollBaseFragment {\n\n    private QMUIContinuousNestedTopDelegateLayout mTopDelegateLayout;\n    private QMUIContinuousNestedTopRecyclerView mTopRecyclerView;\n    private RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopDelegateLayout = new QMUIContinuousNestedTopDelegateLayout(getContext());\n        mTopDelegateLayout.setBackgroundColor(Color.LTGRAY);\n        mTopRecyclerView = new QMUIContinuousNestedTopRecyclerView(getContext());\n\n        AppCompatTextView headerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        headerView.setTextSize(17);\n        headerView.setBackgroundColor(Color.GRAY);\n        headerView.setTextColor(Color.WHITE);\n        headerView.setText(\"This is Top Header\");\n        headerView.setGravity(Gravity.CENTER);\n        mTopDelegateLayout.setHeaderView(headerView);\n\n        AppCompatTextView footerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        footerView.setTextSize(17);\n        footerView.setBackgroundColor(Color.GRAY);\n        footerView.setTextColor(Color.WHITE);\n        footerView.setGravity(Gravity.CENTER);\n        footerView.setText(\"This is Top Footer\");\n        mTopDelegateLayout.setFooterView(footerView);\n\n        mTopDelegateLayout.setDelegateView(mTopRecyclerView);\n\n\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopDelegateLayout, topLp);\n\n        mRecyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mRecyclerView, recyclerViewLp);\n\n        mTopRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mTopRecyclerView.setAdapter(mAdapter);\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\",\n                \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\",\n                \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll5Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopDelegateLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopWebView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"(header + webview + bottom) + recyclerview\")\npublic class QDContinuousNestedScroll5Fragment extends QDContinuousNestedScrollBaseFragment {\n\n    private QMUIContinuousNestedTopDelegateLayout mTopDelegateLayout;\n    private QMUIContinuousNestedTopWebView mTopWebView;\n    private RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopDelegateLayout = new QMUIContinuousNestedTopDelegateLayout(getContext());\n        mTopDelegateLayout.setBackgroundColor(Color.LTGRAY);\n        mTopWebView = new QMUIContinuousNestedTopWebView(getContext());\n\n        AppCompatTextView headerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        headerView.setTextSize(17);\n        headerView.setBackgroundColor(Color.GRAY);\n        headerView.setTextColor(Color.WHITE);\n        headerView.setText(\"This is Top Header\");\n        headerView.setGravity(Gravity.CENTER);\n        mTopDelegateLayout.setHeaderView(headerView);\n\n        final AppCompatTextView footerView = new AppCompatTextView(getContext());\n        footerView.setTextSize(17);\n        footerView.setBackgroundColor(Color.GRAY);\n        footerView.setTextColor(Color.WHITE);\n        footerView.setGravity(Gravity.CENTER);\n        footerView.setText(\"点击展开更多\\nThis is Top Footer\\nThis is Top Footer\\nThis is Top Footer\\n\");\n        footerView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                CharSequence text = footerView.getText();\n                footerView.setText(\"\" + text + text);\n            }\n        });\n        mTopDelegateLayout.setFooterView(footerView);\n\n        mTopDelegateLayout.setDelegateView(mTopWebView);\n\n\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopDelegateLayout, topLp);\n\n        mRecyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mRecyclerView, recyclerViewLp);\n\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mTopWebView.loadUrl(\"https://mp.weixin.qq.com/s/zgfLOMD2JfZJKfHx-5BsBg\");\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\",\n                \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\",\n                \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mTopWebView != null) {\n            mCoordinatorLayout.removeView(mTopWebView);\n            mTopWebView.destroy();\n            mTopWebView = null;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll6Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomRecyclerView;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopLinearLayout;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"linearLayout + recyclerview\")\npublic class QDContinuousNestedScroll6Fragment extends QDContinuousNestedScrollBaseFragment {\n\n    private QMUIContinuousNestedTopLinearLayout mTopLinearLayout;\n    private RecyclerView mRecyclerView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopLinearLayout = new QMUIContinuousNestedTopLinearLayout(getContext());\n        mTopLinearLayout.setBackgroundColor(Color.LTGRAY);\n        mTopLinearLayout.setOrientation(LinearLayout.VERTICAL);\n\n\n        AppCompatTextView firstView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 1000), View.MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        firstView.setTextSize(17);\n        firstView.setBackgroundColor(Color.DKGRAY);\n        firstView.setTextColor(Color.WHITE);\n        firstView.setText(\"This is Top firstView\");\n        firstView.setGravity(Gravity.CENTER);\n        mTopLinearLayout.addView(firstView);\n\n        AppCompatTextView secondView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 1000), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        secondView.setTextSize(17);\n        secondView.setBackgroundColor(Color.GRAY);\n        secondView.setTextColor(Color.WHITE);\n        secondView.setGravity(Gravity.CENTER);\n        secondView.setText(\"This is secondView\");\n        mTopLinearLayout.addView(secondView);\n\n\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(\n                matchParent, ViewGroup.LayoutParams.WRAP_CONTENT);\n        topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopLinearLayout, topLp);\n\n        mRecyclerView = new QMUIContinuousNestedBottomRecyclerView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mRecyclerView, recyclerViewLp);\n\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\",\n                \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\",\n                \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll7Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopDelegateLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopWebView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\n@Widget(group = Group.Other, name = \"(header + webview + bottom) + (part sticky header + viewpager)\")\npublic class QDContinuousNestedScroll7Fragment extends QDContinuousNestedScrollBaseFragment {\n    private static final String TAG = \"ContinuousNestedScroll\";\n\n    private QMUIContinuousNestedTopDelegateLayout mTopDelegateLayout;\n    private QMUIContinuousNestedTopWebView mNestedWebView;\n    private QDContinuousBottomView mBottomView;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopDelegateLayout = new QMUIContinuousNestedTopDelegateLayout(getContext());\n        mTopDelegateLayout.setBackgroundColor(Color.LTGRAY);\n        mNestedWebView = new QMUIContinuousNestedTopWebView(getContext());\n\n        AppCompatTextView headerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        headerView.setTextSize(17);\n        headerView.setBackgroundColor(Color.GRAY);\n        headerView.setTextColor(Color.WHITE);\n        headerView.setText(\"This is Top Header\");\n        headerView.setGravity(Gravity.CENTER);\n        mTopDelegateLayout.setHeaderView(headerView);\n\n        AppCompatTextView footerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        footerView.setTextSize(17);\n        footerView.setBackgroundColor(Color.GRAY);\n        footerView.setTextColor(Color.WHITE);\n        footerView.setGravity(Gravity.CENTER);\n        footerView.setText(\"This is Top Footer\");\n        mTopDelegateLayout.setFooterView(footerView);\n\n        mTopDelegateLayout.setDelegateView(mNestedWebView);\n\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopDelegateLayout, topLp);\n\n        mBottomView = new QDContinuousBottomView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mBottomView, recyclerViewLp);\n\n        mNestedWebView.loadUrl(\"https://mp.weixin.qq.com/s/zgfLOMD2JfZJKfHx-5BsBg\");\n\n        mCoordinatorLayout.addOnScrollListener(new QMUIContinuousNestedScrollLayout.OnScrollListener() {\n            @Override\n            public void onScroll(QMUIContinuousNestedScrollLayout scrollLayout, int topCurrent, int topRange, int offsetCurrent,\n                                 int offsetRange, int bottomCurrent, int bottomRange) {\n                Log.i(TAG, String.format(\"topCurrent = %d; topRange = %d; \" +\n                                \"offsetCurrent = %d; offsetRange = %d; \" +\n                                \"bottomCurrent = %d, bottomRange = %d\",\n                        topCurrent, topRange, offsetCurrent, offsetRange, bottomCurrent, bottomRange));\n            }\n\n            @Override\n            public void onScrollStateChange(QMUIContinuousNestedScrollLayout scrollLayout, int newScrollState, boolean fromTopBehavior) {\n                Log.i(TAG, String.format(\"newScrollState = %d; fromTopBehavior = %b\",\n                        newScrollState, fromTopBehavior));\n            }\n        });\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mNestedWebView != null) {\n            mCoordinatorLayout.removeView(mNestedWebView);\n            mNestedWebView.destroy();\n            mNestedWebView = null;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScroll8Fragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.graphics.Color;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedBottomAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopAreaBehavior;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopDelegateLayout;\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedTopRecyclerView;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmuidemo.base.BaseRecyclerAdapter;\nimport com.qmuiteam.qmuidemo.base.RecyclerViewHolder;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n@Widget(group = Group.Other, name = \"(header + recyclerView + bottom) + (part sticky header + viewpager)\")\npublic class QDContinuousNestedScroll8Fragment extends QDContinuousNestedScrollBaseFragment {\n    private static final String TAG = \"ContinuousNestedScroll\";\n\n    private QMUIContinuousNestedTopDelegateLayout mTopDelegateLayout;\n    private QMUIContinuousNestedTopRecyclerView mTopRecyclerView;\n    private QDContinuousBottomView mBottomView;\n    private BaseRecyclerAdapter<String> mAdapter;\n\n    @Override\n    protected void initCoordinatorLayout() {\n        mTopDelegateLayout = new QMUIContinuousNestedTopDelegateLayout(getContext());\n        mTopDelegateLayout.setBackgroundColor(Color.LTGRAY);\n        new QMUIContinuousNestedTopRecyclerView(getContext());\n\n        AppCompatTextView headerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        headerView.setTextSize(17);\n        headerView.setBackgroundColor(Color.GRAY);\n        headerView.setTextColor(Color.WHITE);\n        headerView.setText(\"This is Top Header\");\n        headerView.setGravity(Gravity.CENTER);\n        mTopDelegateLayout.setHeaderView(headerView);\n\n        AppCompatTextView footerView = new AppCompatTextView(getContext()) {\n            @Override\n            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n                super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                        QMUIDisplayHelper.dp2px(getContext(), 100), MeasureSpec.EXACTLY\n                ));\n            }\n        };\n        footerView.setTextSize(17);\n        footerView.setBackgroundColor(Color.GRAY);\n        footerView.setTextColor(Color.WHITE);\n        footerView.setGravity(Gravity.CENTER);\n        footerView.setText(\"This is Top Footer\");\n        mTopDelegateLayout.setFooterView(footerView);\n\n        mTopRecyclerView = new QMUIContinuousNestedTopRecyclerView(getContext());\n        mTopRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()) {\n            @Override\n            public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.WRAP_CONTENT);\n            }\n        });\n        mTopDelegateLayout.setDelegateView(mTopRecyclerView);\n\n        int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;\n        CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(getContext()));\n        mCoordinatorLayout.setTopAreaView(mTopDelegateLayout, topLp);\n\n        mBottomView = new QDContinuousBottomView(getContext());\n        CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams(\n                matchParent, matchParent);\n        recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior());\n        mCoordinatorLayout.setBottomAreaView(mBottomView, recyclerViewLp);\n\n        mCoordinatorLayout.addOnScrollListener(new QMUIContinuousNestedScrollLayout.OnScrollListener() {\n            @Override\n            public void onScroll(QMUIContinuousNestedScrollLayout scrollLayout, int topCurrent, int topRange, int offsetCurrent,\n                                 int offsetRange, int bottomCurrent, int bottomRange) {\n                Log.i(TAG, String.format(\"topCurrent = %d; topRange = %d; \" +\n                                \"offsetCurrent = %d; offsetRange = %d; \" +\n                                \"bottomCurrent = %d, bottomRange = %d\",\n                        topCurrent, topRange, offsetCurrent, offsetRange, bottomCurrent, bottomRange));\n            }\n\n            @Override\n            public void onScrollStateChange(QMUIContinuousNestedScrollLayout scrollLayout, int newScrollState, boolean fromTopBehavior) {\n\n            }\n        });\n\n        mAdapter = new BaseRecyclerAdapter<String>(getContext(), null) {\n            @Override\n            public int getItemLayoutId(int viewType) {\n                return android.R.layout.simple_list_item_1;\n            }\n\n            @Override\n            public void bindData(RecyclerViewHolder holder, int position, String item) {\n                holder.setText(android.R.id.text1, item);\n            }\n        };\n        mAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(View itemView, int pos) {\n                Toast.makeText(getContext(), \"click position=\" + pos, Toast.LENGTH_SHORT).show();\n            }\n        });\n        mTopRecyclerView.setAdapter(mAdapter);\n        onDataLoaded();\n    }\n\n    private void onDataLoaded() {\n        List<String> data = new ArrayList<>(Arrays.asList(\"Helps\", \"Maintain\", \"Liver\",\n                \"Health\", \"Function\", \"Supports\", \"Healthy\", \"Fat\", \"Metabolism\", \"Nuturally\",\n                \"Bracket\", \"Refrigerator\", \"Bathtub\", \"Wardrobe\", \"Comb\", \"Apron\", \"Carpet\",\n                \"Bolster\", \"Pillow\", \"Cushion\"));\n        Collections.shuffle(data);\n        mAdapter.setData(data);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScrollBaseFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\npublic abstract class QDContinuousNestedScrollBaseFragment extends BaseFragment {\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBarLayout;\n    @BindView(R.id.pull_to_refresh) QMUIPullRefreshLayout mPullRefreshLayout;\n    @BindView(R.id.coordinator) QMUIContinuousNestedScrollLayout mCoordinatorLayout;\n\n    private Bundle mSavedScrollInfo = new Bundle();\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_continuous_nested_scroll, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n        initPullRefreshLayout();\n        initCoordinatorLayout();\n        mCoordinatorLayout.setDraggableScrollBarEnabled(true);\n        return view;\n    }\n\n    private void initPullRefreshLayout(){\n        mPullRefreshLayout.setOnPullListener(new QMUIPullRefreshLayout.OnPullListener() {\n            @Override\n            public void onMoveTarget(int offset) {\n\n            }\n\n            @Override\n            public void onMoveRefreshView(int offset) {\n\n            }\n\n            @Override\n            public void onRefresh() {\n                mPullRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mPullRefreshLayout.finishRefresh();\n                    }\n                }, 3000);\n            }\n        });\n    }\n\n    private void initTopBar() {\n        mTopBarLayout.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBarLayout.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n        mTopBarLayout.addRightTextButton(\"scroll\", QMUIViewHelper.generateViewId())\n                .setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        showBottomSheet();\n                    }\n                });\n    }\n\n    protected abstract void initCoordinatorLayout();\n\n    private void showBottomSheet() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getContext())\n                .addItem(\"scrollToBottom\")\n                .addItem(\"scrollToTop\")\n                .addItem(\"scrollBottomViewToTop\")\n                .addItem(\"scrollBy 40dp\")\n                .addItem(\"scrollBy -40dp\")\n                .addItem(\"smoothScrollBy 100dp/1s\")\n                .addItem(\"smoothScrollBy -100dp/1s\")\n                .addItem(\"save current scroll info\")\n                .addItem(\"restore scroll info\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        switch (position) {\n                            case 0:\n                                mCoordinatorLayout.scrollToBottom();\n                                break;\n                            case 1:\n                                mCoordinatorLayout.scrollToTop();\n                                break;\n                            case 2:\n                                mCoordinatorLayout.scrollBottomViewToTop();\n                                break;\n                            case 3:\n                                mCoordinatorLayout.scrollBy(QMUIDisplayHelper.dp2px(getContext(), 40));\n                                break;\n                            case 4:\n                                mCoordinatorLayout.scrollBy(QMUIDisplayHelper.dp2px(getContext(), -40));\n                                break;\n                            case 5:\n                                mCoordinatorLayout.smoothScrollBy(QMUIDisplayHelper.dp2px(getContext(), 100), 1000);\n                                break;\n                            case 6:\n                                mCoordinatorLayout.smoothScrollBy(QMUIDisplayHelper.dp2px(getContext(), -100), 1000);\n                                break;\n                            case 7:\n                                mCoordinatorLayout.saveScrollInfo(mSavedScrollInfo);\n                                break;\n                            case 8:\n                                mCoordinatorLayout.restoreScrollInfo(mSavedScrollInfo);\n                        }\n                        dialog.dismiss();\n                    }\n                })\n                .build().show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDContinuousNestedScrollFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport androidx.annotation.Nullable;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Lab,\n        widgetClass = QMUIContinuousNestedScrollLayout.class,\n        iconRes = R.mipmap.icon_grid_continuous_nest_scroll,\n        docUrl =\"https://github.com/Tencent/QMUI_Android/wiki/QMUIContinuousNestedScrollLayout\")\npublic class QDContinuousNestedScrollFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mQDDataManager = QDDataManager.getInstance();\n    }\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n        initGroupListView();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDDataManager.getName(this.getClass()));\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll1Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll1Fragment fragment = new QDContinuousNestedScroll1Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll2Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll2Fragment fragment = new QDContinuousNestedScroll2Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll3Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll3Fragment fragment = new QDContinuousNestedScroll3Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll4Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll4Fragment fragment = new QDContinuousNestedScroll4Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll5Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll5Fragment fragment = new QDContinuousNestedScroll5Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll6Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll6Fragment fragment = new QDContinuousNestedScroll6Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll7Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll7Fragment fragment = new QDContinuousNestedScroll7Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDContinuousNestedScroll8Fragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDContinuousNestedScroll8Fragment fragment = new QDContinuousNestedScroll8Fragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDEditorFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.unit.dp\nimport com.qmuiteam.compose.core.ui.QMUITopBar\nimport com.qmuiteam.compose.core.ui.QMUITopBarBackIconItem\nimport com.qmuiteam.editor.*\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.ComposeBaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.launch\n\n@Widget(name = \"QMUI Editor\", iconRes = R.mipmap.icon_grid_in_progress)\n@LatestVisitRecord\nclass QDEditorFragment : ComposeBaseFragment() {\n\n    @Composable\n    fun TextButton(text: String ,onClick: ()-> Unit){\n        Text(text, modifier = Modifier\n            .clickable {\n                onClick()\n            }\n            .padding(8.dp))\n    }\n\n    @Composable\n    fun QDEditor() {\n\n        val channel = remember {\n            Channel<EditorBehavior>()\n        }\n        val scope = rememberCoroutineScope()\n        Column(modifier = Modifier.fillMaxSize()) {\n            QMUIEditor(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .weight(1f)\n                    .padding(16.dp),\n                value = TextFieldValue(\"\"),\n                hint = AnnotatedString(\"写下这一刻的想法\"),\n                channel = channel\n            ) {\n\n            }\n\n            Row(modifier = Modifier\n                .fillMaxWidth()\n                .height(60.dp)) {\n                TextButton(\"加粗\"){\n                    scope.launch {\n                        channel.send(BoldBehavior(500))\n                    }\n\n                }\n\n                TextButton(\"引用\"){\n                    scope.launch {\n                        channel.send(QuoteBehavior)\n                    }\n                }\n\n                TextButton(\"无序列表\"){\n                    scope.launch {\n                        channel.send(UnOrderListBehavior)\n                    }\n                }\n\n                TextButton(\"Header\"){\n                    scope.launch {\n                        channel.send(HeaderBehavior(HeaderLevel.h2))\n                    }\n                }\n            }\n        }\n\n    }\n\n    @Composable\n    override fun PageContent() {\n        Column(modifier = Modifier.fillMaxSize()) {\n            QMUITopBar(\n                title = \"QMUIEditor\",\n                leftItems = arrayListOf(\n                    QMUITopBarBackIconItem {\n                        popBackStack()\n                    }\n                )\n            )\n            Box(modifier = Modifier\n                .fillMaxWidth()\n                .weight(1f)) {\n                QDEditor()\n            }\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDEmojiInputFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab\n\nimport android.content.Context\nimport android.text.Editable\nimport android.text.TextWatcher\nimport android.util.Log\nimport android.view.Gravity\nimport android.view.View\nimport android.widget.EditText\nimport android.widget.FrameLayout\nimport android.widget.LinearLayout\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatEditText\nimport androidx.constraintlayout.widget.ConstraintLayout\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmui.kotlin.*\nimport com.qmuiteam.qmui.type.parser.EmojiTextParser\nimport com.qmuiteam.qmui.type.view.EmojiEditText\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout\nimport com.qmuiteam.qmuidemo.QDQQFaceManager\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.BaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\n\n@Widget(name = \"EmojiEditText\", iconRes = R.mipmap.icon_grid_in_progress)\n@LatestVisitRecord\nclass QDEmojiInputFragment : BaseFragment() {\n    override fun onCreateView(): View {\n        return EmojiLayout(requireContext())\n    }\n}\n\nclass EmojiLayout(context: Context): ConstraintLayout(context){\n    val topBarLayout = QMUITopBarLayout(context).apply {\n        fitsSystemWindows = true\n        id = View.generateViewId()\n    }\n    val editText = EmojiEditText(context).apply {\n        gravity = Gravity.TOP or Gravity.LEFT\n        textParser = EmojiTextParser(QDQQFaceManager.getInstance()) { true }\n    }\n\n    val se = TextView(context).apply {\n        text = \"[色]\"\n        setPadding(0, dip(20), 0, dip(20))\n        onClick {\n            editText.replaceSelection(\"[色]\")\n        }\n    }\n    val weixiao = TextView(context).apply {\n        text = \"[微笑]\"\n        setPadding(0, dip(20), 0, dip(20))\n        onClick {\n            editText.replaceSelection(\"[微笑]\")\n        }\n    }\n    val daku = TextView(context).apply {\n        text = \"[大哭]\"\n        setPadding(0, dip(20), 0, dip(20))\n        onClick {\n            editText.replaceSelection(\"[大哭]\")\n        }\n    }\n    val delete = TextView(context).apply {\n        text = \"delete\"\n        setPadding(0, dip(20), 0, dip(20))\n        onClick {\n            editText.delete()\n        }\n    }\n    val toolBar = LinearLayout(context).apply {\n        id = View.generateViewId()\n        orientation = LinearLayout.HORIZONTAL\n        addView(se, LinearLayout.LayoutParams(0, wrapContent, 1f))\n        addView(weixiao, LinearLayout.LayoutParams(0, wrapContent, 1f))\n        addView(daku, LinearLayout.LayoutParams(0, wrapContent, 1f))\n        addView(delete, LinearLayout.LayoutParams(0, wrapContent, 1f))\n    }\n\n    init {\n        addView(topBarLayout, LayoutParams(0, wrapContent).apply {\n            alignParentHor()\n            topToTop = constraintParentId\n        })\n        addView(toolBar, LayoutParams(0, wrapContent).apply {\n            alignParentHor()\n            bottomToBottom = constraintParentId\n        })\n\n        addView(editText, LayoutParams(0, 0).apply {\n            alignParentHor()\n            topToBottom = topBarLayout.id\n            bottomToTop = toolBar.id\n        })\n\n        editText.setText(\"反反复复[微笑][色]发发发方法\")\n    }\n\n    fun handleClick(text: String){\n        val origin = editText.text\n        if(origin == null){\n            editText.setText(text)\n        }else{\n            origin.append(text)\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDPhotoClipFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab\n\nimport android.graphics.Bitmap\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.sp\nimport androidx.core.net.toUri\nimport com.qmuiteam.photo.coil.QMUICoilPhotoProvider\nimport com.qmuiteam.photo.compose.QMUIPhotoClipper\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.ComposeBaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\n\n@Widget(name = \"QMUI Photo Clip\", iconRes = R.mipmap.icon_grid_in_progress)\n@LatestVisitRecord\nclass QDPhotoClipFragment : ComposeBaseFragment() {\n\n    @Composable\n    override fun PageContent() {\n        var ret by remember {\n            mutableStateOf<Bitmap?>(null)\n        }\n        QMUIPhotoClipper(\n            photoProvider = QMUICoilPhotoProvider(\n                \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                0f\n            )\n        ) { doClip ->\n            Row(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .align(Alignment.BottomCenter)\n            ) {\n                Box(modifier = Modifier\n                    .weight(1f)\n                    .clickable {\n                        popBackStack()\n                    }\n                    .padding(vertical = 16.dp),\n                    contentAlignment = Alignment.Center\n                ) {\n                    Text(\n                        \"取消\",\n                        fontSize = 20.sp,\n                        color = Color.White,\n                        fontWeight = FontWeight.Bold\n                    )\n                }\n                Box(modifier = Modifier\n                    .weight(1f)\n                    .clickable {\n                        ret = doClip()\n                    }\n                    .padding(vertical = 16.dp),\n                    contentAlignment = Alignment.Center\n                ) {\n                    Text(\n                        \"确定\",\n                        fontSize = 20.sp,\n                        color = Color.White,\n                        fontWeight = FontWeight.Bold\n                    )\n                }\n            }\n\n            ret?.let {\n                Image(bitmap = it.asImageBitmap(), contentDescription = \"\")\n            }\n\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDPhotoFragment.kt",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab\n\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.rememberLazyListState\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.asImageBitmap\nimport androidx.compose.ui.graphics.painter.BitmapPainter\nimport androidx.compose.ui.unit.dp\nimport androidx.core.net.toUri\nimport androidx.lifecycle.lifecycleScope\nimport com.qmuiteam.compose.core.ui.QMUITopBarBackIconItem\nimport com.qmuiteam.compose.core.ui.QMUITopBarTextItem\nimport com.qmuiteam.compose.core.ui.QMUITopBarWithLazyScrollState\nimport com.qmuiteam.photo.activity.QMUIPhotoPickResult\nimport com.qmuiteam.photo.activity.QMUIPhotoPickerActivity\nimport com.qmuiteam.photo.activity.getQMUIPhotoPickResult\nimport com.qmuiteam.photo.coil.QMUICoilPhotoProvider\nimport com.qmuiteam.photo.coil.QMUIMediaCoilPhotoProviderFactory\nimport com.qmuiteam.photo.compose.QMUIPhotoThumbnailWithViewer\nimport com.qmuiteam.photo.util.QMUIPhotoHelper\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord\nimport com.qmuiteam.qmuidemo.R\nimport com.qmuiteam.qmuidemo.base.ComposeBaseFragment\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n@Widget(name = \"QMUI Photo\", iconRes = R.mipmap.icon_grid_in_progress)\n@LatestVisitRecord\nclass QDPhotoFragment : ComposeBaseFragment() {\n\n    val pickerFlow = MutableStateFlow<QMUIPhotoPickResult?>(null)\n\n    private val pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {\n        if (it.resultCode == RESULT_OK) {\n            val pickerResult = it.data?.getQMUIPhotoPickResult() ?: return@registerForActivityResult\n            pickerFlow.value = pickerResult\n        }\n    }\n\n    @Composable\n    override fun PageContent() {\n        Column(modifier = Modifier.fillMaxSize()) {\n            val scrollState = rememberLazyListState()\n            QMUITopBarWithLazyScrollState(\n                scrollState = scrollState,\n                title = \"QMUIPhoto\",\n                leftItems = arrayListOf(\n                    QMUITopBarBackIconItem {\n                        popBackStack()\n                    }\n                ),\n                rightItems = arrayListOf(\n                    QMUITopBarTextItem(\"Pick a Picture\") {\n                        val activity = activity ?: return@QMUITopBarTextItem\n                        pickLauncher.launch(\n                            QMUIPhotoPickerActivity.intentOf(\n                                activity,\n                                QMUIPhotoPickerActivity::class.java,\n                                QMUIMediaCoilPhotoProviderFactory::class.java\n                            )\n                        )\n\n                    }\n                )\n            )\n            LazyColumn(\n                state = scrollState,\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .weight(1f)\n                    .background(Color.White),\n                contentPadding = PaddingValues(start = 44.dp)\n            ) {\n\n                item {\n                    PickerResult()\n                }\n\n//                item {\n//                    TestImageCompress()\n//                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                                    1f\n                                )\n                            )\n                        )\n                    }\n\n                }\n\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                )\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"file:///android_asset/test.png\".toUri(),\n                                    0.0125f\n                                )\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                )\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                )\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                                    1f\n                                )\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                                    1f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                            )\n                        )\n                    }\n                }\n\n                item {\n                    Box(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(horizontal = 20.dp, vertical = 20.dp)\n                    ) {\n                        QMUIPhotoThumbnailWithViewer(\n                            activity = requireActivity(),\n                            images = listOf(\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                                    1f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9136/1yn0KLFwy6Vb0nE6Sg.png\".toUri(),\n                                    1.379f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/8779/6WY7guGLeGfp0KK6Sb.jpeg\".toUri(),\n                                    0.749f\n                                ),\n                                QMUICoilPhotoProvider(\n                                    \"https://weread-picture-1258476243.file.myqcloud.com/9979/31y68oGufDGL3zQ6TT.jpg\".toUri(),\n                                    1f\n                                ),\n                            )\n                        )\n                    }\n                }\n            }\n        }\n    }\n\n    @Composable\n    fun PickerResult() {\n        val pickResultState = pickerFlow.collectAsState()\n        val pickResult = pickResultState.value\n        if (pickResult == null) {\n            Box(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(horizontal = 20.dp, vertical = 20.dp)\n                    .clickable {\n                        val activity = activity ?: return@clickable\n                        pickLauncher.launch(\n                            QMUIPhotoPickerActivity.intentOf(\n                                activity,\n                                QMUIPhotoPickerActivity::class.java,\n                                QMUIMediaCoilPhotoProviderFactory::class.java\n                            )\n                        )\n                    }\n            ) {\n                Text(\"No Picked Images, click to pick\")\n            }\n        } else {\n            val images = remember(pickResult) {\n                pickResult.list.map {\n                    QMUICoilPhotoProvider(\n                        it.uri,\n                        it.ratio()\n                    )\n                }\n            }\n            Column(\n                modifier = Modifier\n                    .fillMaxWidth()\n                    .padding(horizontal = 20.dp, vertical = 20.dp)\n            ) {\n                Text(text = \"原图：${pickResult.isOriginOpen}\")\n                QMUIPhotoThumbnailWithViewer(\n                    activity = requireActivity(),\n                    images = images\n                )\n            }\n        }\n\n\n    }\n\n    @Composable\n    fun TestImageCompress() {\n        var bitmap by remember {\n            mutableStateOf<Bitmap?>(null)\n        }\n        LaunchedEffect(\"\") {\n            lifecycleScope.launch {\n                withContext(Dispatchers.IO) {\n                    QMUIPhotoHelper.compressByShortEdgeWidthAndByteSize(\n                        requireContext(),\n                        {\n                            it.assets.open(\"test.png\")\n                        },\n                        500\n                    )?.inputStream().use {\n                        if (it != null) {\n                            bitmap = BitmapFactory.decodeStream(it)\n                        }\n                    }\n                }\n            }\n        }\n\n        if (bitmap != null) {\n            Image(painter = BitmapPainter(bitmap!!.asImageBitmap()), contentDescription = \"\")\n        }\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDSchemeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.EditText;\n\nimport com.qmuiteam.qmui.arch.annotation.LatestVisitRecord;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.manager.QDSchemeManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@LatestVisitRecord\n@Widget(group = Group.Lab, name = \"Scheme\", iconRes = R.mipmap.icon_grid_in_progress)\npublic class QDSchemeFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n\n    @BindView(R.id.edit_text)\n    EditText mEditText;\n\n    @BindView(R.id.button)\n    QMUIRoundButton mBtn;\n\n    @Override\n    protected View onCreateView() {\n        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_scheme, null);\n        ButterKnife.bind(this, view);\n        initTopBar();\n        initEditStuff();\n        return view;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getDescription(this.getClass()).getName());\n    }\n\n    private void initEditStuff() {\n        mBtn.setEnabled(false);\n        mEditText.addTextChangedListener(new TextWatcher() {\n            @Override\n            public void beforeTextChanged(CharSequence s, int start, int count, int after) {\n\n            }\n\n            @Override\n            public void onTextChanged(CharSequence s, int start, int before, int count) {\n\n            }\n\n            @Override\n            public void afterTextChanged(Editable s) {\n                if (s == null || s.length() == 0) {\n                    mBtn.setEnabled(false);\n                }\n                mBtn.setEnabled(true);\n            }\n        });\n        mBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QDSchemeManager.getInstance().handle(mEditText.getText().toString());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDSnapHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.PagerSnapHelper;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.recyclerview.widget.SnapHelper;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.adaptor.QDRecyclerViewAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/** 使用 {@link SnapHelper} 实现 {@link RecyclerView} 按页滚动。\n * Created by cgspine on 15/9/15.\n */\n\n@Widget(group = Group.Lab, name = \"用SnapHelper实现RecyclerView按页滚动\", iconRes = R.mipmap.icon_grid_pager_layout_manager)\npublic class QDSnapHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.pagerWrap) ViewGroup mPagerWrap;\n\n    RecyclerView mRecyclerView;\n    LinearLayoutManager mPagerLayoutManager;\n    QDRecyclerViewAdapter mRecyclerViewAdapter;\n    SnapHelper mSnapHelper;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getContext()).inflate(R.layout.fragment_pagerlayoutmanager, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        mRecyclerView = new RecyclerView(getContext());\n        mPagerLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);\n        mRecyclerView.setLayoutManager(mPagerLayoutManager);\n        mRecyclerViewAdapter = new QDRecyclerViewAdapter();\n        mRecyclerViewAdapter.setItemCount(10);\n        mRecyclerView.setAdapter(mRecyclerViewAdapter);\n        mPagerWrap.addView(mRecyclerView);\n        // PagerSnapHelper每次只能滚动一个item;用LinearSnapHelper则可以一次滚动多个，并最终保证定位\n        // mSnapHelper = new LinearSnapHelper();\n        mSnapHelper = new PagerSnapHelper();\n        mSnapHelper.attachToRecyclerView(mRecyclerView);\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        // 切换其他情况的按钮\n        mTopBar.addRightImageButton(R.mipmap.icon_topbar_overflow, R.id.topbar_right_change_button).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                showBottomSheetList();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void showBottomSheetList() {\n        new QMUIBottomSheet.BottomListSheetBuilder(getActivity())\n                .addItem(\"水平方向\")\n                .addItem(\"垂直方向\")\n                .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() {\n                    @Override\n                    public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) {\n                        dialog.dismiss();\n                        switch (position) {\n                            case 0:\n                                mPagerLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);\n                                break;\n                            case 1:\n                                mPagerLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);\n                                break;\n                            default:\n                                break;\n                        }\n                    }\n                })\n                .build()\n                .show();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDSwipeDeleteListViewFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport androidx.collection.LongSparseArray;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\nimport android.widget.ListView;\n\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.adaptor.QDSimpleAdapter;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * @author cginechen\n * @date 2017-03-29\n */\n\n@Widget(group = Group.Lab, name = \"ListView滑动删除\")\npublic class QDSwipeDeleteListViewFragment extends BaseFragment {\n    private static final int SWIPE_DURATION = 250;\n    private static final int MOVE_DURATION = 150;\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.listview) ListView mListView;\n    LongSparseArray<Integer> mItemIdTopMap = new LongSparseArray<>();\n\n    private MyAdapter mAdapter;\n\n    boolean mSwiping = false;\n    boolean mItemPressed = false;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getContext()).inflate(R.layout.fragment_swipe_delete_listview, null);\n        ButterKnife.bind(this, root);\n        initTopBar();\n        initListView();\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initListView() {\n        List<String> data = new ArrayList<>();\n        for (int i = 0; i < 20; i++) {\n            data.add(\"item \" + (i + 1));\n        }\n        mAdapter = new MyAdapter(getContext(), data, new View.OnTouchListener() {\n\n            float mDownX;\n            private int mSwipeSlop = -1;\n\n            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n            @Override\n            public boolean onTouch(final View v, MotionEvent event) {\n                if (mSwipeSlop < 0) {\n                    mSwipeSlop = ViewConfiguration.get(QDSwipeDeleteListViewFragment.this.getContext()).\n                            getScaledTouchSlop();\n                }\n                switch (event.getAction()) {\n                    case MotionEvent.ACTION_DOWN:\n                        if (mItemPressed) {\n                            return false;\n                        }\n                        mItemPressed = true;\n                        mDownX = event.getX();\n                        break;\n                    case MotionEvent.ACTION_CANCEL:\n                        v.setAlpha(1);\n                        v.setTranslationX(0);\n                        mItemPressed = false;\n                        break;\n                    case MotionEvent.ACTION_MOVE: {\n                        float x = event.getX() + v.getTranslationX();\n                        float deltaX = x - mDownX;\n                        float deltaXAbs = Math.abs(deltaX);\n                        if (!mSwiping) {\n                            if (deltaXAbs > mSwipeSlop) {\n                                mSwiping = true;\n                                mListView.requestDisallowInterceptTouchEvent(true);\n                            }\n                        }\n                        if (mSwiping) {\n                            v.setTranslationX((x - mDownX));\n                            v.setAlpha(1 - deltaXAbs / v.getWidth());\n                        }\n                    }\n                    break;\n                    case MotionEvent.ACTION_UP: {\n                        if (mSwiping) {\n                            float x = event.getX() + v.getTranslationX();\n                            float deltaX = x - mDownX;\n                            float deltaXAbs = Math.abs(deltaX);\n                            float fractionCovered;\n                            float endX;\n                            float endAlpha;\n                            final boolean remove;\n                            if (deltaXAbs > v.getWidth() / 4) {\n                                // Greater than a quarter of the width - animate it out\n                                fractionCovered = deltaXAbs / v.getWidth();\n                                endX = deltaX < 0 ? -v.getWidth() : v.getWidth();\n                                endAlpha = 0;\n                                remove = true;\n                            } else {\n                                // Not far enough - animate it back\n                                fractionCovered = 1 - (deltaXAbs / v.getWidth());\n                                endX = 0;\n                                endAlpha = 1;\n                                remove = false;\n                            }\n                            // Animate position and alpha of swiped item\n                            // NOTE: This is a simplified version of swipe behavior, for the\n                            // purposes of this demo about animation. A real version should use\n                            // velocity (via the VelocityTracker class) to send the item off or\n                            // back at an appropriate speed.\n                            long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);\n                            mListView.setEnabled(false);\n                            v.animate().setDuration(duration).\n                                    alpha(endAlpha).translationX(endX).\n                                    withEndAction(new Runnable() {\n                                        @Override\n                                        public void run() {\n                                            // Restore animated values\n                                            v.setAlpha(1);\n                                            v.setTranslationX(0);\n                                            if (remove) {\n                                                animateRemoval(mListView, v);\n                                            } else {\n                                                mSwiping = false;\n                                                mListView.setEnabled(true);\n                                            }\n                                        }\n                                    });\n                        }\n                    }\n                    mItemPressed = false;\n                    break;\n                    default:\n                        return false;\n                }\n                return true;\n            }\n        });\n        mListView.setAdapter(mAdapter);\n    }\n\n    private void animateRemoval(final ListView listview, View viewToRemove) {\n        int firstVisiblePosition = listview.getFirstVisiblePosition();\n        for (int i = 0; i < listview.getChildCount(); ++i) {\n            View child = listview.getChildAt(i);\n            if (child != viewToRemove) {\n                int position = firstVisiblePosition + i;\n                long itemId = mAdapter.getItemId(position);\n                mItemIdTopMap.put(itemId, child.getTop());\n            }\n        }\n        // Delete the item from the adapter\n        int position = mListView.getPositionForView(viewToRemove);\n        mAdapter.remove(position);\n        final ViewTreeObserver observer = listview.getViewTreeObserver();\n        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {\n            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n            public boolean onPreDraw() {\n                observer.removeOnPreDrawListener(this);\n                boolean firstAnimation = true;\n                int firstVisiblePosition = listview.getFirstVisiblePosition();\n                for (int i = 0; i < listview.getChildCount(); ++i) {\n                    final View child = listview.getChildAt(i);\n                    int position = firstVisiblePosition + i;\n                    long itemId = mAdapter.getItemId(position);\n                    Integer startTop = mItemIdTopMap.get(itemId);\n                    int top = child.getTop();\n                    if (startTop != null) {\n                        if (startTop != top) {\n                            int delta = startTop - top;\n                            child.setTranslationY(delta);\n                            child.animate().setDuration(MOVE_DURATION).translationY(0);\n                            if (firstAnimation) {\n                                child.animate().withEndAction(new Runnable() {\n                                    public void run() {\n                                        mSwiping = false;\n                                        mListView.setEnabled(true);\n                                    }\n                                });\n                                firstAnimation = false;\n                            }\n                        }\n                    } else {\n                        // Animate new views along with the others. The catch is that they did not\n                        // exist in the start state, so we must calculate their starting position\n                        // based on neighboring views.\n                        int childHeight = child.getHeight() + listview.getDividerHeight();\n                        startTop = top + (i > 0 ? childHeight : -childHeight);\n                        int delta = startTop - top;\n                        child.setTranslationY(delta);\n                        child.animate().setDuration(MOVE_DURATION).translationY(0);\n                        if (firstAnimation) {\n                            child.animate().withEndAction(new Runnable() {\n                                public void run() {\n                                    mSwiping = false;\n                                    mListView.setEnabled(true);\n                                }\n                            });\n                            firstAnimation = false;\n                        }\n                    }\n                }\n                mItemIdTopMap.clear();\n                return true;\n            }\n        });\n    }\n\n    private static class MyAdapter extends QDSimpleAdapter {\n        private View.OnTouchListener mTouchListener;\n\n        public MyAdapter(Context context, List<String> data, View.OnTouchListener listener) {\n            super(context, data);\n            mTouchListener = listener;\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return getItem(position).hashCode();\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            View view = super.getView(position, convertView, parent);\n            view.setOnTouchListener(mTouchListener);\n            return view;\n        }\n\n        @Override\n        public boolean hasStableIds() {\n            return true;\n        }\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDWebViewBridgeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.annotation.TargetApi;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebView;\nimport android.widget.Toast;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;\nimport com.qmuiteam.qmui.widget.webview.QMUIBridgeWebViewClient;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewBridgeHandler;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewClient;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewContainer;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDSchemeManager;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Widget(group = Group.Other, name = \"Webview Bridge\")\npublic class QDWebViewBridgeFragment extends QDWebExplorerFragment {\n\n    public QDWebViewBridgeFragment() {\n        String url = \"file:///android_asset/demo.html\";\n        Bundle bundle = new Bundle();\n        bundle.putString(EXTRA_URL, url);\n        bundle.putString(EXTRA_TITLE, \"测试 Bridge\");\n        setArguments(bundle);\n    }\n\n    @Override\n    protected boolean needDispatchSafeAreaInset() {\n        return false;\n    }\n\n\n    @Override\n    protected void configWebView(QMUIWebViewContainer webViewContainer, QMUIWebView webView) {\n        webView.setCallback(new QMUIWebView.Callback() {\n            @Override\n            public void onSureNotSupportChangeCssEnv() {\n                new QMUIDialog.MessageDialogBuilder(getContext())\n                        .setMessage(\"Do not support to change css env\")\n                        .addAction(new QMUIDialogAction(getContext(), R.string.ok, new QMUIDialogAction.ActionListener() {\n\n                            @Override\n                            public void onClick(QMUIDialog dialog, int index) {\n                                dialog.dismiss();\n                            }\n                        }))\n                        .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                        .show();\n            }\n        });\n    }\n\n    @Override\n    protected WebChromeClient getWebViewChromeClient() {\n        return new ExplorerWebViewChromeClient(this) {\n            @Override\n            public void onShowCustomView(View view, CustomViewCallback callback) {\n                super.onShowCustomView(view, callback);\n                mTopBarLayout.setBackgroundAlpha(0);\n            }\n\n            @Override\n            public void onHideCustomView() {\n                super.onHideCustomView();\n            }\n        };\n    }\n\n    @Override\n    protected QMUIWebViewClient getWebViewClient() {\n        QMUIWebViewBridgeHandler handler = new QMUIWebViewBridgeHandler(mWebView) {\n\n            @Override\n            protected List<String> getSupportedCmdList() {\n                List<String> ret = new ArrayList<>();\n                ret.add(\"test\");\n                return ret;\n            }\n\n            @Override\n            protected void handleMessage(String message, MessageFinishCallback callback) {\n                try {\n                    JSONObject json = new JSONObject(message);\n                    String id = json.getString(\"id\");\n                    String info = json.getString(\"info\");\n                    Toast.makeText(getContext(), \"id = \" + id + \"; info = \" + info, Toast.LENGTH_SHORT).show();\n                    JSONObject result = new JSONObject();\n                    result.put(\"code\", 100);\n                    result.put(\"message\", \"Native 的执行结果\");\n                    callback.finish(result);\n                } catch (JSONException e) {\n                    e.printStackTrace();\n                    callback.finish(null);\n                }\n            }\n\n        };\n        return new QMUIBridgeWebViewClient(needDispatchSafeAreaInset(), false, handler){\n            @Override\n            @TargetApi(21)\n            protected boolean onShouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n                if(QDSchemeManager.getInstance().handle(request.getUrl().toString())){\n                    return true;\n                }\n                return super.onShouldOverrideUrlLoading(view, request);\n            }\n\n            @Override\n            protected boolean onShouldOverrideUrlLoading(WebView view, String url) {\n                if(QDSchemeManager.getInstance().handle(url)){\n                    return true;\n                }\n                return super.onShouldOverrideUrlLoading(view, url);\n            }\n        };\n    }\n\n    @Override\n    protected void onScrollWebContent(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n        mTopBarLayout.computeAndSetBackgroundAlpha(scrollY, 0, QMUIDisplayHelper.dp2px(getContext(), 20));\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDWebViewFixFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.os.Bundle;\nimport android.view.View;\nimport android.webkit.WebChromeClient;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebViewContainer;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.fragment.QDWebExplorerFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\n@Widget(group = Group.Other, name = \"修复 css-env-safe-area-inset\")\npublic class QDWebViewFixFragment extends QDWebExplorerFragment {\n\n    public QDWebViewFixFragment() {\n        String url = \"http://cgsdream.org/static/html/test-css-env-safe-area-inset.html\";\n        Bundle bundle = new Bundle();\n        bundle.putString(EXTRA_URL, url);\n        bundle.putString(EXTRA_TITLE, \"test-css-env-safe-area-inset\");\n        setArguments(bundle);\n    }\n\n    @Override\n    protected boolean needDispatchSafeAreaInset() {\n        return true;\n    }\n\n\n    @Override\n    protected void configWebView(QMUIWebViewContainer webViewContainer, QMUIWebView webView) {\n        webView.setCallback(new QMUIWebView.Callback() {\n            @Override\n            public void onSureNotSupportChangeCssEnv() {\n                new QMUIDialog.MessageDialogBuilder(getContext())\n                        .setMessage(\"Do not support to change css env\")\n                        .addAction(new QMUIDialogAction(getContext(), R.string.ok, new QMUIDialogAction.ActionListener() {\n\n                            @Override\n                            public void onClick(QMUIDialog dialog, int index) {\n                                dialog.dismiss();\n                            }\n                        }))\n                        .setSkinManager(QMUISkinManager.defaultInstance(getContext()))\n                        .show();\n            }\n        });\n    }\n\n    @Override\n    protected WebChromeClient getWebViewChromeClient() {\n        return new ExplorerWebViewChromeClient(this) {\n            @Override\n            public void onShowCustomView(View view, CustomViewCallback callback) {\n                super.onShowCustomView(view, callback);\n                mTopBarLayout.setBackgroundAlpha(0);\n            }\n\n            @Override\n            public void onHideCustomView() {\n                super.onHideCustomView();\n            }\n        };\n    }\n\n    @Override\n    protected void onScrollWebContent(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n        mTopBarLayout.computeAndSetBackgroundAlpha(scrollY, 0, QMUIDisplayHelper.dp2px(getContext(), 20));\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/lab/QDWebViewFragment.java",
    "content": "package com.qmuiteam.qmuidemo.fragment.lab;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n@Widget(group = Group.Lab, widgetClass = QMUIWebView.class, iconRes = R.mipmap.icon_grid_webview)\npublic class QDWebViewFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDDataManager mQDDataManager;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDDataManager = QDDataManager.getInstance();\n        mQDItemDescription = mQDDataManager.getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDWebViewFixFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDWebViewFixFragment fragment = new QDWebViewFixFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(mQDDataManager.getName(\n                        QDWebViewBridgeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDWebViewBridgeFragment fragment = new QDWebViewBridgeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDColorHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport androidx.core.content.ContextCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.LinearLayout;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIColorHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIColorHelper} 的使用示例。\n * Created by Kayo on 2016/12/1.\n */\n\n@Widget(group = Group.Helper, widgetClass = QMUIColorHelper.class, iconRes = R.mipmap.icon_grid_color_helper)\npublic class QDColorHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.square_alpha) View mAlphaView;\n    @BindView(R.id.square_desc_alpha) TextView mAlphaTextView;\n    @BindView(R.id.ratioSeekBar) SeekBar mRatioSeekBar;\n    @BindView(R.id.transformTextView) TextView mTransformTextView;\n    @BindView(R.id.ratioSeekBarWrap) LinearLayout mRatioSeekBarWrap;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_colorhelper, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initContent() {\n        // 设置颜色的 alpha 值\n        float alpha = 0.5f;\n        int alphaColor = QMUIColorHelper.setColorAlpha(ContextCompat.getColor(getContext(), R.color.colorHelper_square_alpha_background), alpha);\n        mAlphaView.setBackgroundColor(alphaColor);\n        mAlphaTextView.setText(String.format(getResources().getString(R.string.colorHelper_squqre_alpha), alpha));\n\n        // 根据比例，在两个 color 值之间计算出一个 color 值\n        final int fromColor = ContextCompat.getColor(getContext(), R.color.colorHelper_square_from_ratio_background);\n        final int toColor = ContextCompat.getColor(getContext(), R.color.colorHelper_square_to_ratio_background);\n\n        mRatioSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n            @Override\n            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n                int ratioColor = QMUIColorHelper.computeColor(fromColor, toColor, (float) progress / 100);\n                mRatioSeekBarWrap.setBackgroundColor(ratioColor);\n            }\n\n            @Override\n            public void onStartTrackingTouch(SeekBar seekBar) {\n\n            }\n\n            @Override\n            public void onStopTrackingTouch(SeekBar seekBar) {\n\n            }\n        });\n        mRatioSeekBar.setProgress(50);\n\n        // 将 color 颜色值转换为字符串\n        String transformColor = QMUIColorHelper.colorToString(mTransformTextView.getCurrentTextColor());\n        mTransformTextView.setText(String.format(\"这个 TextView 的字体颜色是：%1$s\", transformColor));\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDDeviceHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport androidx.core.content.ContextCompat;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.style.ForegroundColorSpan;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIDeviceHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIDeviceHelper} 的使用示例。\n * Created by Kayo on 2016/12/2.\n */\n\n@Widget(group = Group.Helper, widgetClass = QMUIDeviceHelper.class, iconRes = R.mipmap.icon_grid_device_helper)\npublic class QDDeviceHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private SpannableString getFormatItemValue(CharSequence value) {\n        SpannableString result = new SpannableString(value);\n        result.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.qmui_config_color_gray_5)), 0, value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n        return result;\n    }\n\n    private void initContent() {\n        String isTabletText = booleanToString(QMUIDeviceHelper.isTablet(getContext()));\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(getString(R.string.deviceHelper_tablet_title)), null)\n                .addItemView(mGroupListView.createItemView(getFormatItemValue(String.format(\"当前设备%1$s平板设备\", isTabletText))), null)\n                .addTo(mGroupListView);\n\n        String isFlymeText = booleanToString(QMUIDeviceHelper.isFlyme());\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(getString(R.string.deviceHelper_flyme_title)), null)\n                .addItemView(mGroupListView.createItemView(getFormatItemValue(String.format(\"当前设备%1$s Flyme 系统\", isFlymeText))), null)\n                .addTo(mGroupListView);\n\n        String isMiuiText = booleanToString(QMUIDeviceHelper.isMIUI());\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(getString(R.string.deviceHelper_miui_title)), null)\n                .addItemView(mGroupListView.createItemView(getFormatItemValue(String.format(\"当前设备%1$s MIUI 系统\", isMiuiText))), null)\n                .addTo(mGroupListView);\n\n        String isMeizuText = booleanToString(QMUIDeviceHelper.isMeizu());\n        QMUIGroupListView.newSection(getContext())\n                .addItemView(mGroupListView.createItemView(getString(R.string.deviceHelper_meizu_title)), null)\n                .addItemView(mGroupListView.createItemView(getFormatItemValue(String.format(\"当前设备%1$s魅族手机\", isMeizuText))), null)\n                .addTo(mGroupListView);\n    }\n\n    private String booleanToString(boolean b) {\n        return b ? \"是\" : \"不是\";\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDDrawableHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.GradientDrawable;\nimport android.graphics.drawable.LayerDrawable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIDrawableHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIDrawableHelper} 的使用示例。\n * Created by Kayo on 2016/12/5.\n */\n\n@Widget(group = Group.Helper, widgetClass = QMUIDrawableHelper.class, iconRes = R.mipmap.icon_grid_drawable_helper)\npublic class QDDrawableHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.createFromView) QMUIRoundButton mCreateFromViewButton;\n    @BindView(R.id.solidImage) ImageView mSolidImageView;\n    @BindView(R.id.circleGradient) ImageView mCircleGradientView;\n    @BindView(R.id.tintColor) ImageView mTintColorImageView;\n    @BindView(R.id.tintColorOrigin) ImageView mTintColorOriginImageView;\n    @BindView(R.id.separator) View mSeparatorView;\n\n    private View mRootView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        mRootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_drawablehelper, null);\n        ButterKnife.bind(this, mRootView);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initContent();\n\n        return mRootView;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initContent() {\n        int commonShapeSize = getResources().getDimensionPixelSize(R.dimen.drawableHelper_common_shape_size);\n        int commonShapeRadius = QMUIDisplayHelper.dp2px(getContext(), 10);\n\n        // 创建一张指定大小的纯色图片，支持圆角\n        BitmapDrawable solidImageBitmapDrawable = QMUIDrawableHelper.createDrawableWithSize(getResources(), commonShapeSize, commonShapeSize, commonShapeRadius, ContextCompat.getColor(getContext(), R.color.app_color_theme_3));\n        mSolidImageView.setImageDrawable(solidImageBitmapDrawable);\n\n        // 创建一张圆形渐变图片，支持圆角\n        GradientDrawable gradientCircleGradientDrawable = QMUIDrawableHelper.createCircleGradientDrawable(ContextCompat.getColor(getContext(), R.color.app_color_theme_4),\n                ContextCompat.getColor(getContext(), R.color.qmui_config_color_transparent), commonShapeRadius, 0.5f, 0.5f);\n        mCircleGradientView.setImageDrawable(gradientCircleGradientDrawable);\n\n        // 设置 Drawable 的颜色\n        // 创建两张表现相同的图片\n        BitmapDrawable tintColorBitmapDrawble = QMUIDrawableHelper.createDrawableWithSize(getResources(), commonShapeSize, commonShapeSize, commonShapeRadius, ContextCompat.getColor(getContext(), R.color.app_color_theme_1));\n        BitmapDrawable tintColorOriginBitmapDrawble = QMUIDrawableHelper.createDrawableWithSize(getResources(), commonShapeSize, commonShapeSize, commonShapeRadius, ContextCompat.getColor(getContext(), R.color.app_color_theme_1));\n        // 其中一张重新设置颜色\n        QMUIDrawableHelper.setDrawableTintColor(tintColorBitmapDrawble, ContextCompat.getColor(getContext(), R.color.app_color_theme_7));\n        mTintColorImageView.setImageDrawable(tintColorBitmapDrawble);\n        mTintColorOriginImageView.setImageDrawable(tintColorOriginBitmapDrawble);\n\n        // 创建带上分隔线或下分隔线的 Drawable\n        LayerDrawable separatorLayerDrawable = QMUIDrawableHelper.createItemSeparatorBg(ContextCompat.getColor(getContext(), R.color.app_color_theme_7),\n                ContextCompat.getColor(getContext(), R.color.app_color_theme_6), QMUIDisplayHelper.dp2px(getContext(), 2), true);\n        QMUIViewHelper.setBackgroundKeepingPadding(mSeparatorView, separatorLayerDrawable);\n\n        // 从一个 View 创建 Bitmap\n        mCreateFromViewButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUIDialog.CustomDialogBuilder dialogBuilder = new QMUIDialog.CustomDialogBuilder(getContext());\n                dialogBuilder.setSkinManager(QMUISkinManager.defaultInstance(getContext()));\n                dialogBuilder.setLayout(R.layout.drawablehelper_createfromview);\n                final QMUIDialog dialog = dialogBuilder.setTitle(\"示例效果（点击下图关闭本浮层）\").create();\n                ImageView displayImageView = (ImageView) dialog.findViewById(R.id.createFromViewDisplay);\n                Bitmap createFromViewBitmap = QMUIDrawableHelper.createBitmapFromView(mRootView);\n                displayImageView.setImageBitmap(createFromViewBitmap);\n\n                displayImageView.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        dialog.dismiss();\n                    }\n                });\n\n                dialog.show();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDNotchHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.Window;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.util.QMUIWindowInsetHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.tab.QMUITab;\nimport com.qmuiteam.qmui.widget.tab.QMUITabBuilder;\nimport com.qmuiteam.qmui.widget.tab.QMUITabSegment;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\n\n@Widget(group = Group.Helper, name = \"QMUINotchHelper\", iconRes = R.mipmap.icon_grid_status_bar_helper)\npublic class QDNotchHelperFragment extends BaseFragment {\n    private static final String TAG = \"QDNotchHelperFragment\";\n    @BindView(R.id.not_safe_bg)\n    FrameLayout mNoSafeBgLayout;\n    @BindView(R.id.safe_area_tv)\n    TextView mSafeAreaTv;\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.tabs_container)\n    FrameLayout mTabContainer;\n    @BindView(R.id.tabs)\n    QMUITabSegment mTabSegment;\n\n    boolean isFullScreen = false;\n\n    @OnClick(R.id.safe_area_tv)\n    void onClickTv() {\n        if (isFullScreen) {\n            changeToNotFullScreen();\n        } else {\n            changeToFullScreen();\n        }\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n    }\n\n    @Override\n    protected View onCreateView() {\n        View layout = LayoutInflater.from(getContext()).inflate(R.layout.fragment_notch, null);\n        ButterKnife.bind(this, layout);\n        initTopBar();\n        initTabs();\n        QMUIWindowInsetHelper.handleWindowInsets(mTabContainer,\n                WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.displayCutout(),\n                true,\n                true\n        );\n        mNoSafeBgLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {\n            @Override\n            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {\n                int height = bottom - top;\n                int width = right - left;\n                int screenUsefulWidth = QMUIDisplayHelper.getUsefulScreenWidth(v);\n                int screenUsefulHeight = QMUIDisplayHelper.getUsefulScreenHeight(v);\n                Log.i(TAG, \"width = \" + width + \"; height = \" + height +\n                        \"; screenUsefulWidth = \" + screenUsefulWidth +\n                        \"; screenUsefulHeight = \" + screenUsefulHeight);\n            }\n        });\n        return layout;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initTabs() {\n        QMUITabBuilder builder = mTabSegment.tabBuilder();\n        builder.setColorAttr(R.attr.qmui_config_color_gray_6, R.attr.qmui_config_color_blue)\n                .setSelectedIconScale(2f)\n                .setTextSize(QMUIDisplayHelper.sp2px(getContext(), 14), QMUIDisplayHelper.sp2px(getContext(), 16))\n                .setDynamicChangeIconColor(false);\n        QMUITab component = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_component_selected))\n                .setText(\"Components\")\n                .build(getContext());\n        QMUITab util = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_util))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_util_selected))\n                .setText(\"Helper\")\n                .build(getContext());\n        QMUITab lab = builder\n                .setNormalDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_lab))\n                .setSelectedDrawable(ContextCompat.getDrawable(getContext(), R.mipmap.icon_tabbar_lab_selected))\n                .setText(\"Lab\")\n                .build(getContext());\n\n        mTabSegment.addTab(component)\n                .addTab(util)\n                .addTab(lab);\n        mTabSegment.notifyDataChanged();\n    }\n\n    @Override\n    public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        changeToFullScreen();\n    }\n\n    private void changeToFullScreen() {\n        isFullScreen = true;\n        Activity activity = getActivity();\n        if (activity != null) {\n            Window window = activity.getWindow();\n            if (window == null) {\n                return;\n            }\n            View decorView = window.getDecorView();\n            int systemUi = decorView.getSystemUiVisibility();\n            systemUi |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE\n                    | View.SYSTEM_UI_FLAG_FULLSCREEN\n                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION\n                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;\n            decorView.setSystemUiVisibility(systemUi);\n            QMUIDisplayHelper.setFullScreen(getActivity());\n            QMUIViewHelper.fadeOut(mTopBar, 300, null, true);\n            QMUIViewHelper.fadeOut(mTabContainer, 300, null, true);\n        }\n    }\n\n    private void changeToNotFullScreen() {\n        isFullScreen = false;\n        Activity activity = getActivity();\n        if (activity != null) {\n            Window window = activity.getWindow();\n            if (window == null) {\n                return;\n            }\n            final View decorView = window.getDecorView();\n            int systemUi = decorView.getSystemUiVisibility();\n            systemUi &= ~(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);\n            systemUi |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;\n\n            decorView.setSystemUiVisibility(systemUi);\n            QMUIDisplayHelper.cancelFullScreen(getActivity());\n            QMUIViewHelper.fadeIn(mTopBar, 300, null, true);\n            QMUIViewHelper.fadeIn(mTabContainer, 300, null, true);\n            decorView.post(new Runnable() {\n                @Override\n                public void run() {\n                    ViewCompat.requestApplyInsets(decorView);\n                }\n            });\n\n        }\n\n    }\n\n    @Override\n    protected void popBackStack() {\n        changeToNotFullScreen();\n        super.popBackStack();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDSpanFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport androidx.core.content.ContextCompat;\nimport android.text.Spannable;\nimport android.text.SpannableString;\nimport android.text.style.ImageSpan;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.span.QMUIAlignMiddleImageSpan;\nimport com.qmuiteam.qmui.span.QMUIBlockSpaceSpan;\nimport com.qmuiteam.qmui.span.QMUICustomTypefaceSpan;\nimport com.qmuiteam.qmui.span.QMUIMarginImageSpan;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIDrawableHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmuidemo.QDApplication;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * QMUI 中各种 Span 的使用示例。\n * Created by Kayo on 2016/12/15.\n */\n\n@Widget(group = Group.Component, name = \"Span\", iconRes = R.mipmap.icon_grid_span)\npublic class QDSpanFragment extends BaseFragment {\n\n    /**\n     * 特殊字体 人民币符号\n     */\n    public static Typeface TYPEFACE_RMB;\n\n    static {\n        try {\n            Typeface tmpRmb = Typeface.createFromAsset(QDApplication.getContext().getAssets(),\n                    \"fonts/iconfont.ttf\");\n            TYPEFACE_RMB = Typeface.create(tmpRmb, Typeface.NORMAL);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.alignMiddle) TextView mAlignMiddleTextView;\n    @BindView(R.id.marginImage) TextView mMarginImageTextView;\n    @BindView(R.id.blockSpace) TextView mBlockSpaceTextView;\n    @BindView(R.id.customTypeface) TextView mCustomTypefaceTextView;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_spanhelper, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initContentView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initContentView() {\n        // 支持垂直居中的 ImageSpan\n        int alignMiddleIconLength = QMUIDisplayHelper.dp2px(getContext(), 20);\n        final float spanWidthCharacterCount = 2f;\n        SpannableString spannable = new SpannableString(\"[icon]\" + \"这是一行示例文字，前面的 Span 设置了和文字垂直居中并占 \" + spanWidthCharacterCount + \" 个中文字的宽度\");\n        Drawable iconDrawable = QMUIDrawableHelper.createDrawableWithSize(getResources(), alignMiddleIconLength, alignMiddleIconLength, QMUIDisplayHelper.dp2px(getContext(), 4), ContextCompat.getColor(getContext(), R.color.app_color_theme_3));\n        if (iconDrawable != null) {\n            iconDrawable.setBounds(0, 0, iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight());\n        }\n        ImageSpan alignMiddleImageSpan = new QMUIAlignMiddleImageSpan(iconDrawable, QMUIAlignMiddleImageSpan.ALIGN_MIDDLE, spanWidthCharacterCount);\n        spannable.setSpan(alignMiddleImageSpan, 0, \"[icon]\".length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\n        mAlignMiddleTextView.setText(spannable);\n\n        // 支持增加左右间距的 ImageSpan\n        int marginImageLength = QMUIDisplayHelper.dp2px(getContext(), 20);\n        Drawable marginIcon = QMUIDrawableHelper.createDrawableWithSize(getResources(), marginImageLength, marginImageLength, QMUIDisplayHelper.dp2px(getContext(), 4), ContextCompat.getColor(getContext(), R.color.app_color_theme_5));\n        marginIcon.setBounds(0, 0, marginIcon.getIntrinsicWidth(), marginIcon.getIntrinsicHeight());\n        CharSequence marginImageTextOne = \"左侧内容\";\n        SpannableString marginImageText = new SpannableString(marginImageTextOne + \"[margin]右侧内容\");\n        QMUIMarginImageSpan marginImageSpan = new QMUIMarginImageSpan(marginIcon, QMUIAlignMiddleImageSpan.ALIGN_MIDDLE, QMUIDisplayHelper.dp2px(getContext(), 10), QMUIDisplayHelper.dp2px(getContext(), 10));\n        marginImageText.setSpan(marginImageSpan, marginImageTextOne.length(), marginImageTextOne.length() + \"[margin]\".length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\n        mMarginImageTextView.setText(marginImageText);\n\n        // 整行的空白 Span，可用来用于制作段间距\n        String paragraphFirst = \"这是第一段比较长的段落，演示在段落之间插入段落间距。\\n\";\n        String paragraphSecond = \"这是第二段比较长的段落，演示在段落之间插入段落间距。\";\n        String spaceString = \"[space]\";\n        SpannableString paragraphText = new SpannableString(paragraphFirst + spaceString + paragraphSecond);\n        QMUIBlockSpaceSpan blockSpaceSpan = new QMUIBlockSpaceSpan(QMUIDisplayHelper.dp2px(getContext(), 6));\n        paragraphText.setSpan(blockSpaceSpan, paragraphFirst.length(), paragraphFirst.length() + spaceString.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\n        mBlockSpaceTextView.setText(paragraphText);\n\n        // 自定义部分文字的字体\n        SpannableString customTypefaceText = new SpannableString(getResources().getString(R.string.spanUtils_rmb) + \"100， 前面的人民币符号使用自定义字体特殊处理，对比这个普通的人民币符号: \" + getResources().getString(R.string.spanUtils_rmb));\n        customTypefaceText.setSpan(new QMUICustomTypefaceSpan(\"\", TYPEFACE_RMB), 0, getString(R.string.spanUtils_rmb).length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);\n        mCustomTypefaceTextView.setText(customTypefaceText);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDStatusBarHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.content.Intent;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIStatusBarHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.dialog.QMUITipDialog;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.activity.TranslucentActivity;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIStatusBarHelper} 的使用示例。\n * Created by Kayo on 2016/12/12.\n */\n\n@Widget(group = Group.Helper, widgetClass = QMUIStatusBarHelper.class, iconRes = R.mipmap.icon_grid_status_bar_helper)\npublic class QDStatusBarHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initGroupListView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUIStatusBarHelper.setStatusBarDarkMode(getBaseFragmentActivity()); // 退出界面之前把状态栏还原为白色字体与图标\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initGroupListView() {\n\n        QMUIGroupListView.newSection(getContext())\n                .setDescription(\"支持 4.4 以上版本的 MIUI 和 Flyme，以及 5.0 以上版本的其他 Android\")\n                .addItemView(mGroupListView.createItemView(\"沉浸式状态栏\"), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        Intent intentTranslucent = new Intent(getContext(), TranslucentActivity.class);\n                        startActivity(intentTranslucent);\n                        getActivity().overridePendingTransition(R.anim.slide_in_right, R.anim.slide_still);\n                    }\n                })\n                .addTo(mGroupListView);\n\n        QMUIGroupListView.newSection(getContext())\n                .setDescription(\"支持 4.4 以上版本 MIUI 和 Flyme，以及 6.0 以上版本的其他 Android\")\n                .addItemView(mGroupListView.createItemView(\"设置状态栏黑色字体与图标\"), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QMUIStatusBarHelper.setStatusBarLightMode(getBaseFragmentActivity());\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(\"设置状态栏白色字体与图标\"), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QMUIStatusBarHelper.setStatusBarDarkMode(getBaseFragmentActivity());\n                    }\n                })\n                .addTo(mGroupListView);\n\n        QMUIGroupListView.newSection(getContext())\n                .setDescription(\"不同机型下状态栏高度可能略有差异，并不是固定值，可以通过这个方法获取实际高度\")\n                .addItemView(mGroupListView.createItemView(\"获取状态栏的实际高度\"), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        String result = String.format(getResources().getString(R.string.statusBarHelper_statusBar_height_result), QMUIStatusBarHelper.getStatusbarHeight(getContext()));\n                        final QMUITipDialog tipDialog = new QMUITipDialog.Builder(getContext()).setTipWord(result).create();\n                        tipDialog.show();\n                        mGroupListView.postDelayed(new Runnable() {\n                            @Override\n                            public void run() {\n                                tipDialog.dismiss();\n                            }\n                        }, 1500);\n                    }\n                })\n                .addTo(mGroupListView);\n\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDViewHelperAnimationFadeFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIViewHelper#fadeIn(View, int, Animation.AnimationListener, boolean)} 与\n * {@link QMUIViewHelper#fadeOut(View, int, Animation.AnimationListener, boolean)} 的使用示例。\n * Created by Kayo on 2017/2/7.\n */\n\n@Widget(group = Group.Other, name = \"Fade 进退场动画\")\npublic class QDViewHelperAnimationFadeFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.actiontBtn) QMUIRoundButton mActionButton;\n    @BindView(R.id.popup) TextView mPopupView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_viewhelper_animation_show_and_hide, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initContent() {\n        mActionButton.setText(\"点击显示浮层\");\n        mActionButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if (mPopupView.getVisibility() == View.GONE) {\n                    mActionButton.setText(\"点击关闭浮层\");\n                    mPopupView.setText(\"以 Fade 动画显示本浮层\");\n                    QMUIViewHelper.fadeIn(mPopupView, 500, null, true);\n                } else {\n                    mActionButton.setText(\"点击显示浮层\");\n                    mPopupView.setText(\"以 Fade 动画隐藏本浮层\");\n                    QMUIViewHelper.fadeOut(mPopupView, 500, null, true);\n\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDViewHelperAnimationSlideFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDirection;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIViewHelper#slideIn(View, int, Animation.AnimationListener, boolean, QMUIDirection)} 与\n * {@link QMUIViewHelper#slideOut(View, int, Animation.AnimationListener, boolean, QMUIDirection)} 的使用示例。\n * Created by Kayo on 2017/2/7.\n */\n\n@Widget(group = Group.Other, name = \"Slide 进退场动画\")\npublic class QDViewHelperAnimationSlideFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.actiontBtn) QMUIRoundButton mActionButton;\n    @BindView(R.id.popup) TextView mPopupView;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_viewhelper_animation_show_and_hide, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initContent() {\n        mActionButton.setText(\"点击显示浮层\");\n        mActionButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if (mPopupView.getVisibility() == View.GONE) {\n                    mActionButton.setText(\"点击关闭浮层\");\n                    mPopupView.setText(\"以 Slide 动画显示本浮层\");\n                    QMUIViewHelper.slideIn(mPopupView, 500, null, true, QMUIDirection.TOP_TO_BOTTOM);\n                } else {\n                    mActionButton.setText(\"点击显示浮层\");\n                    mPopupView.setText(\"以 Slide 动画隐藏本浮层\");\n                    QMUIViewHelper.slideOut(mPopupView, 500, null, true, QMUIDirection.BOTTOM_TO_TOP);\n\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDViewHelperBackgroundAnimationBlinkFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport androidx.core.content.ContextCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmui.widget.QMUITopBar;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIViewHelper#playBackgroundBlinkAnimation(View, int)} 的使用示例。\n * Created by Kayo on 2017/2/7.\n */\n\n@Widget(group = Group.Other, name = \"做背景闪动动画\")\npublic class QDViewHelperBackgroundAnimationBlinkFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.actiontBtn) QMUIRoundButton mActionButton;\n    @BindView(R.id.container) ViewGroup mContainer;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_viewhelper_background_animation, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initContent() {\n        mActionButton.setText(\"点击后开始闪动\");\n        mActionButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUIViewHelper.playBackgroundBlinkAnimation(mContainer, ContextCompat.getColor(getContext(), R.color.app_color_theme_3));\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDViewHelperBackgroundAnimationFullFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\n\nimport androidx.core.content.ContextCompat;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIViewHelper#playViewBackgroundAnimation(View, int, int, long)} 的使用示例。\n * Created by Kayo on 2017/2/7.\n */\n\n@Widget(group = Group.Other, name = \"做背景变化动画\")\npublic class QDViewHelperBackgroundAnimationFullFragment extends BaseFragment {\n\n    @BindView(R.id.topbar) QMUITopBarLayout mTopBar;\n    @BindView(R.id.actiontBtn) QMUIRoundButton mActionButton;\n    @BindView(R.id.container) ViewGroup mContainer;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_viewhelper_background_animation, null);\n        ButterKnife.bind(this, root);\n\n        initTopBar();\n        initContent();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(QDDataManager.getInstance().getName(this.getClass()));\n    }\n\n    private void initContent() {\n        mActionButton.setText(\"点击后从黄色背景渐变到绿色背景\");\n        mActionButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                QMUIViewHelper.playViewBackgroundAnimation(mContainer, ContextCompat.getColor(getContext(), R.color.app_color_theme_3), ContextCompat.getColor(getContext(), R.color.app_color_theme_4), 500);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/fragment/util/QDViewHelperFragment.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.fragment.util;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.QMUITopBarLayout;\nimport com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.lib.Group;\nimport com.qmuiteam.qmuidemo.lib.annotation.Widget;\nimport com.qmuiteam.qmuidemo.manager.QDDataManager;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\n\n/**\n * {@link QMUIViewHelper} 内各种方法的使用示例。\n * Created by Kayo on 2017/02/04.\n */\n\n@Widget(group = Group.Helper, widgetClass = QMUIViewHelper.class, iconRes = R.mipmap.icon_grid_view_helper)\npublic class QDViewHelperFragment extends BaseFragment {\n\n    @BindView(R.id.topbar)\n    QMUITopBarLayout mTopBar;\n    @BindView(R.id.groupListView)\n    QMUIGroupListView mGroupListView;\n    private QDItemDescription mQDItemDescription;\n\n    @Override\n    protected View onCreateView() {\n        View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_grouplistview, null);\n        ButterKnife.bind(this, root);\n\n        mQDItemDescription = QDDataManager.getInstance().getDescription(this.getClass());\n        initTopBar();\n\n        initContentView();\n\n        return root;\n    }\n\n    private void initTopBar() {\n        mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                popBackStack();\n            }\n        });\n\n        mTopBar.setTitle(mQDItemDescription.getName());\n    }\n\n    private void initContentView() {\n        QMUIGroupListView.newSection(getContext())\n                .setTitle(\"背景动画\")\n                .addItemView(mGroupListView.createItemView(QDDataManager.getInstance().getName(QDViewHelperBackgroundAnimationBlinkFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDViewHelperBackgroundAnimationBlinkFragment fragment = new QDViewHelperBackgroundAnimationBlinkFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(QDDataManager.getInstance().getName(QDViewHelperBackgroundAnimationFullFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDViewHelperBackgroundAnimationFullFragment fragment = new QDViewHelperBackgroundAnimationFullFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n\n        QMUIGroupListView.newSection(getContext())\n                .setTitle(\"进退场动画\")\n                .addItemView(mGroupListView.createItemView(QDDataManager.getInstance().getName(QDViewHelperAnimationFadeFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDViewHelperAnimationFadeFragment fragment = new QDViewHelperAnimationFadeFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addItemView(mGroupListView.createItemView(QDDataManager.getInstance().getName(QDViewHelperAnimationSlideFragment.class)), new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        QDViewHelperAnimationSlideFragment fragment = new QDViewHelperAnimationSlideFragment();\n                        startFragment(fragment);\n                    }\n                })\n                .addTo(mGroupListView);\n    }\n\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDAppGlideModule.kt",
    "content": "package com.qmuiteam.qmuidemo.manager\n\nimport com.bumptech.glide.annotation.GlideModule\nimport com.bumptech.glide.module.AppGlideModule\n\n@GlideModule\nclass QDAppGlideModule: AppGlideModule() {\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDDataManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.manager;\n\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\nimport com.qmuiteam.qmuidemo.fragment.QDDialogFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDBottomSheetFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDButtonFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDCollapsingTopBarLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDEmptyViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDFloatLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDGroupListViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDLinkTextViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDPopupFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDPriorityLinearLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDProgressBarFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDPullRefreshFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDRadiusImageViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDRecyclerViewDraggableScrollBarFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.swipeAction.QDRVSwipeActionFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDSliderFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDSpanTouchFixTextViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDTabSegmentFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDTipDialogFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.QDVerticalTextViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.pullLayout.QDPullFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.qqface.QDQQFaceFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.section.QDSectionLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.components.viewpager.QDViewPagerFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDAnimationListViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDArchTestFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDComposeTipFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDContinuousNestedScrollFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDEditorFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDEmojiInputFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDPhotoClipFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDPhotoFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDSchemeFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDSnapHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDWebViewFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDColorHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDDeviceHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDDrawableHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDNotchHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDSpanFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDStatusBarHelperFragment;\nimport com.qmuiteam.qmuidemo.fragment.util.QDViewHelperFragment;\nimport com.qmuiteam.qmuidemo.model.QDItemDescription;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author cginechen\n * @date 2016-10-21\n */\n\npublic class QDDataManager {\n    private static QDDataManager _sInstance;\n    private QDWidgetContainer mWidgetContainer;\n\n    private List<Class<? extends BaseFragment>> mComponentsNames;\n    private List<Class<? extends BaseFragment>> mUtilNames;\n    private List<Class<? extends BaseFragment>> mLabNames;\n\n    public QDDataManager() {\n        mWidgetContainer = QDWidgetContainer.getInstance();\n        initComponentsDesc();\n        initUtilDesc();\n        initLabDesc();\n    }\n\n    public static QDDataManager getInstance() {\n        if (_sInstance == null) {\n            _sInstance = new QDDataManager();\n        }\n        return _sInstance;\n    }\n\n\n    /**\n     * Components\n     */\n    private void initComponentsDesc() {\n        mComponentsNames = new ArrayList<>();\n        mComponentsNames.add(QDButtonFragment.class);\n        mComponentsNames.add(QDDialogFragment.class);\n        mComponentsNames.add(QDFloatLayoutFragment.class);\n        mComponentsNames.add(QDEmptyViewFragment.class);\n        mComponentsNames.add(QDTabSegmentFragment.class);\n        mComponentsNames.add(QDProgressBarFragment.class);\n        mComponentsNames.add(QDBottomSheetFragment.class);\n        mComponentsNames.add(QDGroupListViewFragment.class);\n        mComponentsNames.add(QDTipDialogFragment.class);\n        mComponentsNames.add(QDRadiusImageViewFragment.class);\n        mComponentsNames.add(QDVerticalTextViewFragment.class);\n        mComponentsNames.add(QDPullRefreshFragment.class);\n        mComponentsNames.add(QDPopupFragment.class);\n        mComponentsNames.add(QDSpanTouchFixTextViewFragment.class);\n        mComponentsNames.add(QDLinkTextViewFragment.class);\n        mComponentsNames.add(QDQQFaceFragment.class);\n        mComponentsNames.add(QDSpanFragment.class);\n        mComponentsNames.add(QDCollapsingTopBarLayoutFragment.class);\n        mComponentsNames.add(QDViewPagerFragment.class);\n        mComponentsNames.add(QDLayoutFragment.class);\n        mComponentsNames.add(QDPriorityLinearLayoutFragment.class);\n        mComponentsNames.add(QDSectionLayoutFragment.class);\n        mComponentsNames.add(QDContinuousNestedScrollFragment.class);\n        mComponentsNames.add(QDSliderFragment.class);\n        mComponentsNames.add(QDPullFragment.class);\n        mComponentsNames.add(QDRecyclerViewDraggableScrollBarFragment.class);\n        mComponentsNames.add(QDRVSwipeActionFragment.class);\n    }\n\n    /**\n     * Helper\n     */\n    private void initUtilDesc() {\n        mUtilNames = new ArrayList<>();\n        mUtilNames.add(QDColorHelperFragment.class);\n        mUtilNames.add(QDDeviceHelperFragment.class);\n        mUtilNames.add(QDDrawableHelperFragment.class);\n        mUtilNames.add(QDStatusBarHelperFragment.class);\n        mUtilNames.add(QDViewHelperFragment.class);\n        mUtilNames.add(QDNotchHelperFragment.class);\n    }\n\n    /**\n     * Lab\n     */\n    private void initLabDesc() {\n        mLabNames = new ArrayList<>();\n        mLabNames.add(QDAnimationListViewFragment.class);\n        mLabNames.add(QDSnapHelperFragment.class);\n        mLabNames.add(QDArchTestFragment.class);\n        mLabNames.add(QDWebViewFragment.class);\n        mLabNames.add(QDSchemeFragment.class);\n        mLabNames.add(QDComposeTipFragment.class);\n        mLabNames.add(QDPhotoFragment.class);\n        mLabNames.add(QDPhotoClipFragment.class);\n        mLabNames.add(QDEditorFragment.class);\n        mLabNames.add(QDEmojiInputFragment.class);\n    }\n\n    public QDItemDescription getDescription(Class<? extends BaseFragment> cls) {\n        return mWidgetContainer.get(cls);\n    }\n\n    public String getName(Class<? extends BaseFragment> cls) {\n        QDItemDescription itemDescription = getDescription(cls);\n        if (itemDescription == null) {\n            return null;\n        }\n        return itemDescription.getName();\n    }\n\n    public String getDocUrl(Class<? extends BaseFragment> cls) {\n        QDItemDescription itemDescription = getDescription(cls);\n        if (itemDescription == null) {\n            return null;\n        }\n        return itemDescription.getDocUrl();\n    }\n\n    public List<QDItemDescription> getComponentsDescriptions() {\n        List<QDItemDescription> list = new ArrayList<>();\n        for (int i = 0; i < mComponentsNames.size(); i++) {\n            list.add(mWidgetContainer.get(mComponentsNames.get(i)));\n        }\n        return list;\n    }\n\n    public List<QDItemDescription> getUtilDescriptions() {\n        List<QDItemDescription> list = new ArrayList<>();\n        for (int i = 0; i < mUtilNames.size(); i++) {\n            list.add(mWidgetContainer.get(mUtilNames.get(i)));\n        }\n        return list;\n    }\n\n    public List<QDItemDescription> getLabDescriptions() {\n        List<QDItemDescription> list = new ArrayList<>();\n        for (int i = 0; i < mLabNames.size(); i++) {\n            list.add(mWidgetContainer.get(mLabNames.get(i)));\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDPreferenceManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.manager;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.preference.PreferenceManager;\n\n/**\n * Created by cgspine on 2018/1/14.\n */\n\npublic class QDPreferenceManager {\n    private static SharedPreferences sPreferences;\n    private static QDPreferenceManager sQDPreferenceManager = null;\n\n    private static final String APP_VERSION_CODE = \"app_version_code\";\n    private static final String APP_SKIN_INDEX = \"app_skin_index\";\n\n    private QDPreferenceManager(Context context) {\n        sPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());\n    }\n\n    public static final QDPreferenceManager getInstance(Context context) {\n        if (sQDPreferenceManager == null) {\n            sQDPreferenceManager = new QDPreferenceManager(context);\n        }\n        return sQDPreferenceManager;\n    }\n\n    public void setAppVersionCode(int code) {\n        final SharedPreferences.Editor editor = sPreferences.edit();\n        editor.putInt(APP_VERSION_CODE, code);\n        editor.apply();\n    }\n\n    public int getVersionCode() {\n        return sPreferences.getInt(APP_VERSION_CODE, QDUpgradeManager.INVALIDATE_VERSION_CODE);\n    }\n\n    public void setSkinIndex(int index) {\n        SharedPreferences.Editor editor = sPreferences.edit();\n        editor.putInt(APP_SKIN_INDEX, index);\n        editor.apply();\n    }\n\n    public int getSkinIndex() {\n        return sPreferences.getInt(APP_SKIN_INDEX, QDSkinManager.SKIN_BLUE);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDSchemeManager.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.manager\n\nimport android.app.Activity\nimport android.util.Log\nimport android.widget.Toast\nimport com.qmuiteam.qmui.arch.QMUISwipeBackActivityManager\nimport com.qmuiteam.qmui.arch.scheme.QMUISchemeHandler\nimport com.qmuiteam.qmui.arch.scheme.QMUISchemeHandlerInterceptor\nimport com.qmuiteam.qmui.arch.scheme.QMUISchemeParamValueDecoder\nimport com.qmuiteam.qmui.arch.scheme.SchemeInfo\n\nclass QDSchemeManager private constructor() {\n\n    companion object {\n        private const val TAG = \"QDSchemeManager\"\n        const val SCHEME_PREFIX = \"qmui://\"\n\n        @JvmStatic\n        val instance by lazy { QDSchemeManager() }\n    }\n\n    private val schemeHandler = QMUISchemeHandler.Builder(SCHEME_PREFIX).apply {\n        blockSameSchemeTimeout = 1000\n        interceptorList.add(object : QMUISchemeHandlerInterceptor {\n            override fun intercept(\n                schemeHandler: QMUISchemeHandler,\n                activity: Activity,\n                schemes: List<SchemeInfo>\n            ): Boolean {\n                // Log the scheme.\n                val sb = StringBuilder()\n                for (scheme in schemes) {\n                    sb.append(scheme.origin)\n                    sb.append(\";\")\n                }\n                Log.i(TAG, \"handle scheme: $sb\")\n                return false\n            }\n        })\n        interceptorList.add(QMUISchemeParamValueDecoder())\n    }.build()\n\n    fun handle(scheme: String): Boolean {\n        if (!schemeHandler.handle(scheme)) {\n            Log.i(TAG, \"scheme can not be handled: $scheme\")\n            Toast.makeText(\n                QMUISwipeBackActivityManager.getInstance().currentActivity,\n                \"scheme can not be handled: $scheme\", Toast.LENGTH_SHORT\n            ).show()\n            return false\n        }\n        return true\n    }\n\n    fun handleMuti(schemes:List<String>): Boolean {\n        if(!schemeHandler.handleSchemes(schemes)){\n            Log.i(TAG, \"scheme can not be handled: ${schemes.joinToString(\",\")}\")\n            Toast.makeText(\n                QMUISwipeBackActivityManager.getInstance().currentActivity,\n                \"scheme can not be handled: ${schemes.joinToString(\",\")}\", Toast.LENGTH_SHORT\n            ).show()\n            return false\n        }\n        return true\n    }\n}"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDSkinManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmuidemo.manager;\n\nimport android.content.Context;\nimport android.content.res.Configuration;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmuidemo.QDApplication;\nimport com.qmuiteam.qmuidemo.R;\n\npublic class QDSkinManager {\n    public static final int SKIN_BLUE = 1;\n    public static final int SKIN_DARK = 2;\n    public static final int SKIN_WHITE = 3;\n\n\n    public static void install(Context context) {\n        QMUISkinManager skinManager = QMUISkinManager.defaultInstance(context);\n        skinManager.addSkin(SKIN_BLUE, R.style.app_skin_blue);\n        skinManager.addSkin(SKIN_DARK, R.style.app_skin_dark);\n        skinManager.addSkin(SKIN_WHITE, R.style.app_skin_white);\n        boolean isDarkMode = (context.getResources().getConfiguration().uiMode\n                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;\n        int storeSkinIndex = QDPreferenceManager.getInstance(context).getSkinIndex();\n        if (isDarkMode && storeSkinIndex != SKIN_DARK) {\n            skinManager.changeSkin(SKIN_DARK);\n        } else if (!isDarkMode && storeSkinIndex == SKIN_DARK) {\n            skinManager.changeSkin(SKIN_BLUE);\n        }else{\n            skinManager.changeSkin(storeSkinIndex);\n        }\n    }\n\n    public static void changeSkin(int index) {\n        QMUISkinManager.defaultInstance(QDApplication.getContext()).changeSkin(index);\n        QDPreferenceManager.getInstance(QDApplication.getContext()).setSkinIndex(index);\n    }\n\n    public static int getCurrentSkin() {\n        return QMUISkinManager.defaultInstance(QDApplication.getContext()).getCurrentSkin();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/QDUpgradeManager.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.manager;\n\nimport android.app.Activity;\nimport android.content.Context;\n\n/**\n * Created by cgspine on 2018/1/14.\n */\n\npublic class QDUpgradeManager {\n    public static final int INVALIDATE_VERSION_CODE = 0;\n\n    public static final int VERSION_1_1_0 = 110;\n    public static final int VERSION_1_1_1 = 111;\n    public static final int VERSION_1_1_2 = 112;\n    public static final int VERSION_1_1_3 = 113;\n    public static final int VERSION_1_1_4 = 114;\n    public static final int VERSION_1_1_5 = 115;\n    public static final int VERSION_1_1_6 = 116;\n    public static final int VERSION_1_1_7 = 117;\n    public static final int VERSION_1_1_8 = 118;\n    public static final int VERSION_1_1_9 = 119;\n    public static final int VERSION_1_1_10 = 1110;\n    public static final int VERSION_1_1_11 = 1111;\n    public static final int VERSION_1_1_12 = 1112;\n    public static final int VERSION_1_2_0 = 120;\n    public static final int VERSION_1_3_1 = 131;\n    public static final int VERSION_1_4_0 = 140;\n    public static final int VERSION_2_0_0_alpha1 = -2001;\n    public static final int VERSION_2_0_0_alpha2 = -2002;\n    public static final int VERSION_2_0_0_alpha3 = -2003;\n    public static final int VERSION_2_0_0_alpha4 = -2004;\n    public static final int VERSION_2_0_0_alpha5 = -2005;\n    public static final int VERSION_2_0_0_alpha6 = -2006;\n    public static final int VERSION_2_0_0_alpha7 = -2007;\n    public static final int VERSION_2_0_0_alpha8 = -2008;\n    public static final int VERSION_2_0_0_alpha9 = -2009;\n    public static final int VERSION_2_0_0_alpha10 = -2010;\n    public static final int VERSION_2_0_0_alpha11 = -2011;\n    public static final int VERSION_2_0_1 = 201;\n    private static final int sCurrentVersion = VERSION_2_0_1;\n    private static QDUpgradeManager sQDUpgradeManager = null;\n    private UpgradeTipTask mUpgradeTipTask;\n\n    private Context mContext;\n\n    private QDUpgradeManager(Context context) {\n        mContext = context.getApplicationContext();\n    }\n\n    public static final QDUpgradeManager getInstance(Context context) {\n        if (sQDUpgradeManager == null) {\n            sQDUpgradeManager = new QDUpgradeManager(context);\n        }\n        return sQDUpgradeManager;\n    }\n\n    public void check() {\n        int oldVersion = QDPreferenceManager.getInstance(mContext).getVersionCode();\n        int currentVersion = sCurrentVersion;\n        boolean versionUpdated = false;\n        if(currentVersion != oldVersion){\n            if(currentVersion < 0){\n                // alpha release\n                if(-currentVersion > oldVersion){\n                    versionUpdated = true;\n                }\n            }else if (currentVersion > oldVersion) {\n                versionUpdated = true;\n            }\n        }\n\n        if(versionUpdated){\n            if (oldVersion == INVALIDATE_VERSION_CODE) {\n                onNewInstall(currentVersion);\n            } else {\n                onUpgrade(oldVersion, currentVersion);\n            }\n            QDPreferenceManager.getInstance(mContext).setAppVersionCode(currentVersion);\n        }\n    }\n\n    private void onUpgrade(int oldVersion, int currentVersion) {\n        mUpgradeTipTask = new UpgradeTipTask(oldVersion, currentVersion);\n    }\n\n    private void onNewInstall(int currentVersion) {\n        mUpgradeTipTask = new UpgradeTipTask(INVALIDATE_VERSION_CODE, currentVersion);\n    }\n\n    public void runUpgradeTipTaskIfExist(Activity activity) {\n        if (mUpgradeTipTask != null) {\n            mUpgradeTipTask.upgrade(activity);\n            mUpgradeTipTask = null;\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/UpgradeTask.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.manager;\n\npublic interface UpgradeTask {\n    void upgrade();\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/manager/UpgradeTipTask.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.manager;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.view.View;\n\nimport androidx.core.content.ContextCompat;\n\nimport com.qmuiteam.qmui.skin.QMUISkinManager;\nimport com.qmuiteam.qmui.span.QMUIBlockSpaceSpan;\nimport com.qmuiteam.qmui.span.QMUITouchableSpan;\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIPackageHelper;\nimport com.qmuiteam.qmui.util.QMUIViewHelper;\nimport com.qmuiteam.qmui.widget.dialog.QMUIDialog;\nimport com.qmuiteam.qmuidemo.QDMainActivity;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.fragment.components.section.QDSectionLayoutFragment;\nimport com.qmuiteam.qmuidemo.fragment.lab.QDContinuousNestedScrollFragment;\n\npublic class UpgradeTipTask implements UpgradeTask {\n    private final int mOldVersion;\n    private final int mNewVersion;\n\n    public UpgradeTipTask(int oldVersion, int newVersion) {\n        mOldVersion = oldVersion;\n        mNewVersion = newVersion;\n    }\n\n    @Override\n    public void upgrade() {\n        throw new RuntimeException(\"please call upgrade(Activity activity)\");\n    }\n\n    public void upgrade(Activity activity) {\n        String title = String.format(activity.getString(R.string.app_upgrade_tip_title), QMUIPackageHelper.getAppVersion(activity));\n        CharSequence message = getUpgradeWord(activity);\n        new QMUIDialog.MessageDialogBuilder(activity)\n                .setSkinManager(QMUISkinManager.defaultInstance(activity))\n                .setTitle(title)\n                .setMessage(message)\n                .create(R.style.ReleaseDialogTheme)\n                .show();\n    }\n\n    private void appendBlockSpace(Context context, SpannableStringBuilder builder) {\n        int start = builder.length();\n        builder.append(\"[space]\");\n        QMUIBlockSpaceSpan blockSpaceSpan = new QMUIBlockSpaceSpan(QMUIDisplayHelper.dp2px(context, 6));\n        builder.setSpan(blockSpaceSpan, start, builder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n    }\n\n    public CharSequence getUpgradeWord(final Activity activity) {\n        SpannableStringBuilder text = new SpannableStringBuilder();\n        if(mNewVersion == QDUpgradeManager.VERSION_2_0_1){\n            text.append(\"1. Published to MavenCentral.\\n\");\n            text.append(\"2. Updated dep versions.\\n\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha11){\n            text.append(\"1. Feature: Added a new widget: QMUINavFragment.\\n\");\n            text.append(\"2. Remove LazyLifecycle, use maxLifecycle for replacement.\\n\");\n            text.append(\"3. Some bug fixes.\\n\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha10){\n            text.append(\"1. Feature: Added a new widget: QMUISchemeHandler.\\n\");\n            text.append(\"2. Feature: Supported to remove section title if only one section in QMUIStickSectionAdapter.\\n\");\n            text.append(\"3. Feature: Supported to add a QMUISkinApplyListener to View.\\n\");\n            text.append(\"4. Feature: Add a boolean return value for QMUITabSegment#OnTabClickListener to decide to interrupt the event or not.\\n\");\n            text.append(\"5. Some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha9){\n            text.append(\"1. Some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha8){\n            text.append(\"1. Feature: Add new widget QMUISeekBar.\\n\");\n            text.append(\"2. Feature: Provide QMUIFragment#registerEffect to replace startFragmentForResult.\\n\");\n            text.append(\"3. Feature: Provide QMUINavFragment to support child fragment navigation\\n\");\n            text.append(\"4. Feature: Refactor swipe back to support muti direction.\\n\");\n            text.append(\"5. Some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha7){\n            text.append(\"1. Add OnProgressChangeListener for QMUIProgressBar.\\n\");\n            text.append(\"2. Add skin support for CompoundButton.\\n\");\n            text.append(\"3. Some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha6){\n            text.append(\"1. Features: Add new widget QMUITabSegment2 to support ViewPager2.\\n\");\n            text.append(\"2. Remove the skin's default usage.\\n\");\n            text.append(\"3. QMUILayout support radius which is half of the view height or width.\\n\");\n            text.append(\"4. Some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha4){\n            text.append(\"1. Features: Add new widget: QMUIPullLayout.\\n\");\n            text.append(\"2. Features: Add new widget: QMUIRVItemSwipeAction.\\n\");\n            text.append(\"3. Support muti instance for QMUISkinManager.\\n\");\n            text.append(\"4. some bug fixes.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha2){\n            text.append(\"1. Bugfix: Crash Happened on Android 7 and lower.\\n\");\n            text.append(\"2. Bugfix: QMUIBottomSheet overlapped the navigation bar.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_2_0_0_alpha1){\n            text.append(\"1. Migrated the library to Androidx.\\n\");\n            text.append(\"2. Provided dark mode(skin) support. Almost all widgets are covered.\\n\");\n            text.append(\"3. Refactor some widget such as QMUIPopup, QMUITabSegment. Provided more function.\\n\");\n            text.append(\"4. Provided some simple kotlin methods.\");\n        }else if(mNewVersion == QDUpgradeManager.VERSION_1_4_0){\n            text.append(\"1. Updated arch library to 0.6.0. Provide annotation MaybeFirstIn and DefaultFirstFragment.\\n\");\n            text.append(\"2. Updated lint library to 1.1.0 to Support Android Studio 3.4+.\\n\");\n            text.append(\"3. Replaced parent theme of QMUI.Compat with Theme.AppCompat.DayNight.\\n\");\n            text.append(\"4. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"636\", \"642\"\n            };\n            handleIssues(activity, text, issues);\n        }else if(mNewVersion == QDUpgradeManager.VERSION_1_3_1){\n            text.append(\"1. \");\n            addNewWidget(activity, text, \"QMUIContinuousNestedScrollLayout\",\n                    QDDataManager.getInstance().getDocUrl(QDContinuousNestedScrollFragment.class));\n            text.append(\"\\n\");\n            text.append(\"2. \");\n            addNewWidget(activity, text, \"QMUIRadiusImageView2\",\n                    QDDataManager.getInstance().getDocUrl(QDContinuousNestedScrollFragment.class));\n            text.append(\"Implemented with QMUILayout.\\n\");\n            text.append(\"3. Updated arch library to 0.5.0. Fixed issues on new androidx version.\\n\");\n            text.append(\"4. Features: QMUIQQFaceView supports paragraph space when ellipsize at the end.\\n\");\n            text.append(\"5. Features: QMUITabSegment supports space weight.\\n\");\n            text.append(\"6. Features: QMUIPullRefreshLayout added method setToRefreshDirectly().\\n\");\n            text.append(\"7. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"562\", \"563\", \"563\"\n            };\n            handleIssues(activity, text, issues);\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_2_0) {\n            text.append(\"1. \");\n            addNewWidget(activity, text, \"QMUIStickySectionLayout\",\n                    QDDataManager.getInstance().getDocUrl(QDSectionLayoutFragment.class));\n            text.append(\"\\n\");\n            text.append(\"2. Supported startFragmentForResult in child fragment. #499\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_12) {\n            text.append(\"1. Fixed drag issues when refreshing.\\n\");\n            text.append(\"2. Fixed the crash in QMUIPopup under Android 4.4 because of webp.\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_11) {\n            text.append(\"1. Updated arch library to 0.3.0. Now developer must update support library to 28 or use androidx.\\n\");\n            text.append(\"2. Feature: Added custom typeface support in QMUITabSegment.\\n\");\n            text.append(\"3. Fixed a bug that QMUICollapsingTopBarLayout will lose title if swipe back.\\n\");\n            text.append(\"4. Fixed a bug that span click event is not triggered in QMUIQQFaceView. #473\\n\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_10) {\n            text.append(\"1. Simplified the use of QMUIWebContainer.\\n\");\n            text.append(\"2. Refactored QMUITabSegment to handle operations such as reducing item.\\n\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_9) {\n            text.append(\"1. Fixed an error that fitSystemWindows does not work in QMUIWebContainer.\\n\");\n            text.append(\"2. Fixed an error that swiping back would blink.\\n\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_8) {\n            text.append(\"1. Implemented QMUIWebView (beta), where supports for env(safe-area-inset-*) in css were added.\\n\");\n            text.append(\"2. Feature: QMUIQQFaceView supports gravity(left/right/center-horizontal) attribute.\\n\");\n            text.append(\"3. Feature: allows setting shadow color on Android ROM version 9 and higher.\\n\");\n            text.append(\"4. Feature: allows control of the size of left icon in QMUIGroupListView.Section by calling the method setLeftIconSize.\\n\");\n            text.append(\"5. Feature: supports custom web url matcher in QMUILinkify.\\n\");\n            text.append(\"6. Fixed some bugs and increased code robustness.\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_7) {\n            text.append(\"1. Improved QMUINotchHelper to support Xiaomi. \\n\");\n            text.append(\"2. Improved drawing effect of QMUIQQFaceView. \\n\");\n            text.append(\"3. Fixed a bug where UI would become unresponsive \" +\n                    \"if popBackStack was invoked during fragment transitions.\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_6) {\n            text.append(\"1. Feature: QMUINotchHelper, a new helper class for notch compatibility. \\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Added \\\"more\\\" click event to QMUIQQFaceView.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"3. Added text color setter for QMUITouchableSpan.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"4. The method startFragmentAndDestroyCurrent in QMUIFragment supports transfer of target fragment.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"5. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"334\", \"352\"\n            };\n            handleIssues(activity, text, issues);\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_5) {\n            text.append(\"1. Code optimization for QMUIDialog.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Added a return value to KeyboardVisibilityEventListener, which \" +\n                    \"determines whether OnGlobalLayoutListener is deleted.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"3. Bug fix: getSignCount() in QMUITabSegment should return 0 \" +\n                    \"if view is not visible.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"4. Bug fix: fixed incorrect layout of translucent status bar may \" +\n                    \"appear in Android 4.4.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"5. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"304\", \"308\"\n            };\n            handleIssues(activity, text, issues);\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_4) {\n            text.append(\"1. Added a new widget: QMUIPriorityLinearLayout.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Bug fix: marginRight does not make sense for controlling \" +\n                    \"the position of signCount, it should use marginLeft.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"3. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"165\", \"247\"\n            };\n            handleIssues(activity, text, issues);\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_3) {\n            text.append(\"1. Feature: delay validation of QMUIFragment.canDragBack() until a pop \" +\n                    \"gesture occurs. This feature allows you to control pop gesture on the fly.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Replace QMUIMaterialProgressDrawable with CircularProgressDrawable, \" +\n                    \"an official implementation.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"3. Fixed issues: \");\n            final String[] issues = new String[]{\n                    \"254\", \"258\", \"284\", \"285\", \"293\", \"294\"\n            };\n            handleIssues(activity, text, issues);\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_2) {\n            text.append(\"1. Updated arch library to 0.0.4 to fix issue #235.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Added API to get line count in QMUIFloatLayout\");\n        } else if (mNewVersion == QDUpgradeManager.VERSION_1_1_1) {\n            text.append(\"1. Bug fixes: can not read /system/build.prop begin from android 8.0.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Allow custom layout in QMUIPopup.\");\n        } else if (mNewVersion <= QDUpgradeManager.VERSION_1_1_0) {\n            text.append(\"1. Added QMUILayout, making it easy to implement shadows, radii, and separators.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"2. Refactored the theme usage of QMUITopbar.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"3. Refactored QMUIDialog for more flexible configuration.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"4. Updated arch library to 0.0.3 to provide methods runAfterAnimation and startFragmentForResult.\\n\");\n            appendBlockSpace(activity, text);\n            text.append(\"5. Bug fixes: \");\n            final String[] issues = new String[]{\n                    \"125\", \"127\", \"132\", \"141\", \"177\", \"184\", \"198\", \"200\", \"209\", \"213\"\n            };\n            handleIssues(activity, text, issues);\n        } else {\n            text.append(\"welcome to QMUI!\");\n        }\n        return text;\n    }\n\n    private void addNewWidget(final Activity activity, SpannableStringBuilder text, final String widgetName, final String docUrl) {\n        text.append(\"Added a new widget: \");\n        if (docUrl == null || docUrl.length() == 0) {\n            text.append(widgetName);\n        } else {\n            int start = text.length();\n            text.append(widgetName);\n            int end = text.length();\n            text.setSpan(new QMUITouchableSpan(QMUIViewHelper.getActivityRoot(activity),\n                    R.attr.app_skin_span_normal_text_color,\n                    R.attr.app_skin_span_pressed_text_color, 0, 0) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Intent intent = QDMainActivity.createWebExplorerIntent(activity, docUrl, widgetName);\n                    activity.startActivity(intent);\n                }\n            }, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n        }\n\n        text.append(\".\");\n    }\n\n    private void handleIssues(final Activity activity, SpannableStringBuilder text, String[] issues) {\n        final String issueBaseUrl = \"https://github.com/Tencent/QMUI_Android/issues/\";\n        int start, end;\n        for (int i = 0; i < issues.length; i++) {\n            if (i == issues.length - 1) {\n                text.append(\"and \");\n            }\n            final String issue = issues[i];\n            start = text.length();\n            text.append(\"#\");\n            text.append(issue);\n            end = text.length();\n            int normalColor = ContextCompat.getColor(activity, R.color.app_color_blue);\n            int pressedColor = ContextCompat.getColor(activity, R.color.app_color_blue_pressed);\n            text.setSpan(new QMUITouchableSpan(normalColor, pressedColor, 0, 0) {\n                @Override\n                public void onSpanClick(View widget) {\n                    Intent intent = QDMainActivity.createWebExplorerIntent(activity, issueBaseUrl + issue, null);\n                    activity.startActivity(intent);\n                }\n            }, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);\n            if (i < issues.length - 1) {\n                text.append(\", \");\n            } else {\n                text.append(\".\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/model/CustomEffect.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.model;\n\nimport com.qmuiteam.qmui.arch.effect.Effect;\n\npublic class CustomEffect extends Effect {\n    private final String mContent;\n\n    public CustomEffect(String content){\n        mContent = content;\n    }\n\n    public String getContent() {\n        return mContent;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/model/QDItemDescription.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.model;\n\nimport com.qmuiteam.qmuidemo.base.BaseFragment;\n\n/**\n * @author cginechen\n * @date 2016-10-21\n */\n\npublic class QDItemDescription {\n    private Class<? extends BaseFragment> mKitDemoClass;\n    private String mKitName;\n    private int mIconRes;\n    private String mDocUrl;\n\n    public QDItemDescription(Class<? extends BaseFragment> kitDemoClass, String kitName){\n        this(kitDemoClass, kitName, 0, \"\");\n    }\n\n\n    public QDItemDescription(Class<? extends BaseFragment> kitDemoClass, String kitName, int iconRes, String docUrl) {\n        mKitDemoClass = kitDemoClass;\n        mKitName = kitName;\n        mIconRes = iconRes;\n        mDocUrl = docUrl;\n    }\n\n    public Class<? extends BaseFragment> getDemoClass() {\n        return mKitDemoClass;\n    }\n\n    public String getName() {\n        return mKitName;\n    }\n\n    public int getIconRes() {\n        return mIconRes;\n    }\n\n    public String getDocUrl() {\n        return mDocUrl;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/model/SectionHeader.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.model;\n\nimport com.qmuiteam.qmui.widget.section.QMUISection;\n\npublic class SectionHeader implements QMUISection.Model<SectionHeader> {\n    private final String text;\n\n    public SectionHeader(String text){\n        this.text = text;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    @Override\n    public SectionHeader cloneForDiff() {\n        return new SectionHeader(getText());\n    }\n\n    @Override\n    public boolean isSameItem(SectionHeader other) {\n        return text == other.text || (text != null && text.equals(other.text));\n    }\n\n    @Override\n    public boolean isSameContent(SectionHeader other) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/model/SectionItem.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.model;\n\nimport com.qmuiteam.qmui.widget.section.QMUISection;\n\npublic class SectionItem implements QMUISection.Model<SectionItem> {\n    private final String text;\n\n    public SectionItem(String text){\n        this.text = text;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    @Override\n    public SectionItem cloneForDiff() {\n        return new SectionItem(getText());\n    }\n\n    @Override\n    public boolean isSameItem(SectionItem other) {\n        return text == other.text || (text != null && text.equals(other.text));\n    }\n\n    @Override\n    public boolean isSameContent(SectionItem other) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/view/QDLoadingItemView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.view;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.widget.FrameLayout;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.widget.QMUILoadingView;\n\npublic class QDLoadingItemView extends FrameLayout {\n\n    private QMUILoadingView mLoadingView;\n\n    public QDLoadingItemView(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public QDLoadingItemView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        mLoadingView = new QMUILoadingView(context,\n                QMUIDisplayHelper.dp2px(context, 24), Color.LTGRAY);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(\n                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\n        lp.gravity = Gravity.CENTER;\n        addView(mLoadingView, lp);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(\n                QMUIDisplayHelper.dp2px(getContext(), 48), MeasureSpec.EXACTLY));\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        mLoadingView.start();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mLoadingView.stop();\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/view/QDSectionHeaderView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.qmuiteam.qmuidemo.view;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmuidemo.R;\nimport com.qmuiteam.qmuidemo.model.SectionHeader;\n\npublic class QDSectionHeaderView extends LinearLayout {\n\n    private TextView mTitleTv;\n    private ImageView mArrowView;\n\n    private int headerHeight = QMUIDisplayHelper.dp2px(getContext(), 56);\n\n    public QDSectionHeaderView(Context context) {\n        this(context, null);\n    }\n\n    public QDSectionHeaderView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        setOrientation(LinearLayout.HORIZONTAL);\n        setGravity(Gravity.CENTER_VERTICAL);\n        setBackgroundColor(Color.WHITE);\n        int paddingHor = QMUIDisplayHelper.dp2px(context, 24);\n        mTitleTv = new TextView(getContext());\n        mTitleTv.setTextSize(20);\n        mTitleTv.setTextColor(Color.BLACK);\n        mTitleTv.setPadding(paddingHor, 0, paddingHor, 0);\n        addView(mTitleTv, new LinearLayout.LayoutParams(\n                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));\n\n        mArrowView = new AppCompatImageView(context);\n        mArrowView.setImageDrawable(QMUIResHelper.getAttrDrawable(getContext(),\n                R.attr.qmui_common_list_item_chevron));\n        mArrowView.setScaleType(ImageView.ScaleType.CENTER);\n        addView(mArrowView, new LinearLayout.LayoutParams(headerHeight, headerHeight));\n    }\n\n    public ImageView getArrowView() {\n        return mArrowView;\n    }\n\n    public void render(SectionHeader header, boolean isFold) {\n        mTitleTv.setText(header.getText());\n        mArrowView.setRotation(isFold ? 0f : 90f);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec,\n                MeasureSpec.makeMeasureSpec(headerHeight, MeasureSpec.EXACTLY));\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/view/QDShadowAdjustLayout.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.view;\n\nimport android.content.Context;\nimport androidx.customview.widget.ViewDragHelper;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport com.qmuiteam.qmui.layout.QMUIFrameLayout;\nimport com.qmuiteam.qmuidemo.R;\n\n/**\n * Created by cgspine on 2018/3/22.\n */\n\npublic class QDShadowAdjustLayout extends QMUIFrameLayout {\n    ViewDragHelper viewDragHelper;\n\n    public QDShadowAdjustLayout(Context context) {\n        this(context, null);\n    }\n\n    public QDShadowAdjustLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        viewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {\n            @Override\n            public boolean tryCaptureView(View child, int pointerId) {\n                return child.getId() == R.id.layout_for_test;\n            }\n\n            @Override\n            public int clampViewPositionHorizontal(View child, int left, int dx) {\n                return left;\n            }\n\n            @Override\n            public int clampViewPositionVertical(View child, int top, int dy) {\n                return top;\n            }\n        });\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent event) {\n        return viewDragHelper.shouldInterceptTouchEvent(event);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        viewDragHelper.processTouchEvent(event);\n        return true;\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/java/com/qmuiteam/qmuidemo/view/QDWebView.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmuidemo.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.webkit.WebSettings;\n\nimport com.qmuiteam.qmui.util.QMUIDisplayHelper;\nimport com.qmuiteam.qmui.util.QMUIPackageHelper;\nimport com.qmuiteam.qmui.util.QMUIResHelper;\nimport com.qmuiteam.qmui.widget.webview.QMUIWebView;\nimport com.qmuiteam.qmuidemo.BuildConfig;\nimport com.qmuiteam.qmuidemo.R;\n\n/**\n * Created by cgspine on 2017/12/5.\n */\n\npublic class QDWebView extends QMUIWebView {\n\n    public QDWebView(Context context) {\n        this(context, null);\n    }\n\n    public QDWebView(Context context, AttributeSet attrs) {\n        this(context, attrs, android.R.attr.webViewStyle);\n    }\n\n    public QDWebView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    @SuppressLint(\"SetJavaScriptEnabled\")\n    protected void init(Context context) {\n        WebSettings webSettings = getSettings();\n        webSettings.setJavaScriptEnabled(true);\n        webSettings.setSupportZoom(true);\n        webSettings.setBuiltInZoomControls(true);\n        webSettings.setDefaultTextEncodingName(\"GBK\");\n        webSettings.setUseWideViewPort(true);\n        webSettings.setLoadWithOverviewMode(true);\n        webSettings.setDomStorageEnabled(true);\n        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);\n        webSettings.setTextZoom(100);\n        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);\n\n        String screen = QMUIDisplayHelper.getScreenWidth(context) + \"x\" + QMUIDisplayHelper.getScreenHeight(context);\n        String userAgent = \"QMUIDemo/\" + QMUIPackageHelper.getAppVersion(context)\n                + \" (Android; \" + Build.VERSION.SDK_INT\n                + \"; Screen/\" + screen + \"; Scale/\" + QMUIDisplayHelper.getDensity(context) + \")\";\n        String agent = getSettings().getUserAgentString();\n        if (agent == null || !agent.contains(userAgent)) {\n            getSettings().setUserAgentString(agent + \" \" + userAgent);\n        }\n\n        // 开启调试\n        if (BuildConfig.DEBUG) {\n            setWebContentsDebuggingEnabled(true);\n        }\n    }\n\n    public void exec(final String jsCode) {\n        evaluateJavascript(jsCode, null);\n    }\n\n    @Override\n    protected int getExtraInsetTop(float density) {\n        return (int) (QMUIResHelper.getAttrDimen(getContext(), R.attr.qmui_topbar_height) / density);\n    }\n}\n"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_app_color_blue_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/app_color_blue_2_disabled\" android:state_enabled=\"false\"/>\n    <item android:color=\"@color/app_color_blue_2_pressed\" android:state_pressed=\"true\"/>\n    <item android:color=\"@color/app_color_blue_2\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_app_color_blue_3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/app_color_blue_3_disabled\" android:state_enabled=\"false\"/>\n    <item android:color=\"@color/app_color_blue_3_pressed\" android:state_pressed=\"true\"/>\n    <item android:color=\"@color/app_color_blue_3\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_app_color_blue_to_red.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/app_color_theme_1\" android:state_enabled=\"false\"/>\n    <item android:color=\"@color/app_color_theme_1\" android:state_pressed=\"true\"/>\n    <item android:color=\"@color/app_color_blue_2\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_app_color_gray.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_gray_4\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/qmui_config_color_gray_9\" />\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_app_color_gray_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_gray_7\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/qmui_config_color_gray_4\" />\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_btn_blue.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/app_color_blue_disabled\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/app_color_blue_pressed\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/app_color_blue\" />\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_btn_gray.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_gray_5\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/qmui_config_color_gray_6\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/qmui_config_color_gray_1\" />\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/color/s_topbar_btn_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/qmui_config_color_50_white\" android:state_enabled=\"false\"/>\n    <item android:color=\"@color/qmui_config_color_75_white\" android:state_pressed=\"true\"/>\n    <item android:color=\"@color/qmui_config_color_white\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_popup_close_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <group\n      android:name=\"iconGroup\"\n      android:pivotX=\"12\"\n      android:pivotY=\"12\"\n      android:rotation=\"0\">\n\n    <path\n        android:name=\"close\"\n        android:pathData=\"M10.95,12.05L11.95,13.05L6.0303,19.0303L4.9697,17.9697L10.95,12.05ZM4.9697,6.0303L6.0303,4.9697L19.0303,17.9697L17.9697,19.0303L4.9697,6.0303ZM17.9697,4.9697L19.0303,6.0303L13.05,11.95L12.05,10.95L17.9697,4.9697Z\"\n        android:strokeWidth=\"1\"\n        android:fillColor=\"#000000\"\n        android:fillType=\"nonZero\"\n        android:strokeColor=\"#00000000\"/>\n\n  </group>\n\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_popup_close_with_bg_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"oval\">\n            <size\n                android:width=\"48dp\"\n                android:height=\"48dp\" />\n            <solid android:color=\"#ccc\" />\n        </shape>\n    </item>\n    <item\n        android:drawable=\"@drawable/icon_popup_close_dark\"\n        android:gravity=\"center\" />\n\n</layer-list>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_quick_action_copy.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:pathData=\"M8,14l3,0l0,1.5l-3,0z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M8,11l5,0l0,1.5l-5,0z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M5.25,6.5L15.75,6.5C16.1642,6.5 16.5,6.8358 16.5,7.25L16.5,19.75C16.5,20.1642 16.1642,20.5 15.75,20.5L5.25,20.5C4.8358,20.5 4.5,20.1642 4.5,19.75L4.5,7.25C4.5,6.8358 4.8358,6.5 5.25,6.5ZM6,8L6,19L15,19L15,8L6,8Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M7.5,3.5L18.5,3.5C19.0523,3.5 19.5,3.9477 19.5,4.5L19.5,17.5L18,17.5L18,5.75C18,5.3358 17.6642,5 17.25,5L7.5,5L7.5,3.5Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_quick_action_delete_line.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:pathData=\"M15.206,14.793L17.493,16.713L17.87,17.74L16.25,17.74L15.206,14.793ZM9.1,9.669L10.261,10.644L9.35,13.2L13.307,13.2L14.88,14.52L8.89,14.52L7.75,17.74L6.13,17.74L9.1,9.669ZM12.65,3.5L16.456,13.884L14.264,12.045L12.03,5.68L10.798,9.136L9.651,8.173L11.37,3.5L12.65,3.5Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M20.083,14.9902l-0.9642,1.1491l-12.7888,-10.5702l0.9642,-1.1491z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M5,19.5l14,0l0,1.5l-14,0z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_quick_action_dict.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:pathData=\"M10,3C13.866,3 17,6.134 17,10C17,13.866 13.866,17 10,17C6.134,17 3,13.866 3,10C3,6.134 6.134,3 10,3ZM10,4.5C6.9624,4.5 4.5,6.9624 4.5,10C4.5,13.0376 6.9624,15.5 10,15.5C13.0376,15.5 15.5,13.0376 15.5,10C15.5,6.9624 13.0376,4.5 10,4.5Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M14,14L20,20\"\n      android:strokeWidth=\"1.5\"\n      android:fillColor=\"#00000000\"\n      android:strokeColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"/>\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_quick_action_line.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:pathData=\"M5,19.5l14,0l0,1.5l-14,0z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M17.87,17.74L16.25,17.74L15.11,14.52L8.89,14.52L7.75,17.74L6.13,17.74L11.37,3.5L12.65,3.5L17.87,17.74ZM14.67,13.2L12.03,5.68L9.35,13.2L14.67,13.2Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"evenOdd\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/icon_quick_action_share.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:pathData=\"M12,21C7.0294,21 3,16.9706 3,12C3,7.0294 7.0294,3 12,3C16.9706,3 21,7.0294 21,12C21,16.9706 16.9706,21 12,21ZM12,19.75C16.2802,19.75 19.75,16.2802 19.75,12C19.75,7.7198 16.2802,4.25 12,4.25C7.7198,4.25 4.25,7.7198 4.25,12C4.25,16.2802 7.7198,19.75 12,19.75Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M8.6967,3.6255L14.6989,9.7078L14.1651,10.2346L7.9658,3.9526C8.2036,3.8332 8.4474,3.7239 8.6967,3.6255ZM20.3682,8.6809L14.3349,14.7946L13.8011,14.2678L20.0382,7.9475C20.1587,8.1861 20.2689,8.4307 20.3682,8.6809ZM15.208,20.4114L9.3011,14.4257L9.8349,13.8989L15.9457,20.0912C15.7056,20.2085 15.4595,20.3155 15.208,20.4114ZM3.6701,15.4139L9.6651,9.3389L10.1989,9.8657L4.0068,16.1404C3.8842,15.9041 3.7718,15.6618 3.6701,15.4139ZM8.625,20.3458L8.625,12.0667L9.375,12.0667L9.375,20.6112C9.12,20.5335 8.8698,20.4449 8.625,20.3458ZM3.75,9.4017L3.75,8.6517L12.75,8.6517L12.75,9.4017L3.75,9.4017ZM14.625,3.7067L15.375,3.7067L15.375,12.8267L14.625,12.8267L14.625,3.7067ZM20.25,14.7317L20.25,15.4817L11.25,15.4817L11.25,14.7317L20.25,14.7317Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n  <path\n      android:pathData=\"M12,16C9.7909,16 8,14.2091 8,12C8,9.7909 9.7909,8 12,8C14.2091,8 16,9.7909 16,12C16,14.2091 14.2091,16 12,16ZM12,14.5C13.3807,14.5 14.5,13.3807 14.5,12C14.5,10.6193 13.3807,9.5 12,9.5C10.6193,9.5 9.5,10.6193 9.5,12C9.5,13.3807 10.6193,14.5 12,14.5Z\"\n      android:strokeWidth=\"1\"\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:strokeColor=\"#00000000\"/>\n</vector>\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/launcher_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_white\"/>\n    <item android:drawable=\"@mipmap/about_logo\" android:gravity=\"center\"/>\n</layer-list>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/pager_layout_item_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n    <!-- rectangle 表示为矩形 -->\n\n    <!-- 填充的颜色 -->\n    <solid android:color=\"@color/app_color_theme_4\"/>\n\n    <!-- android:radius 圆角的半径 -->\n    <corners android:radius=\"4dp\"/>\n\n</shape>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/s_app_touch_fix_area_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_gray_6\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@color/qmui_config_color_gray_9\" />\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/s_list_item_bg_dark_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_gray_4\" android:state_pressed=\"true\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/s_list_item_bg_dark_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_gray_6\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/qmui_config_color_gray_3\"/>\n</selector>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/tab_panel_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- 在Topbar下方的Tab面板的背景，带底部分割线 -->\n<inset xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:insetLeft=\"@dimen/list_divider_height_negative\"\n    android:insetTop=\"@dimen/list_divider_height_negative\"\n    android:insetRight=\"@dimen/list_divider_height_negative\">\n\n    <shape>\n        <solid android:color=\"@color/tab_panel_bg\" />\n        <stroke\n            android:width=\"@dimen/list_divider_height\"\n            android:color=\"@color/tab_panel_divider\" />\n    </shape>\n</inset>"
  },
  {
    "path": "qmuidemo/src/main/res/drawable/web_explorer_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"@color/qmui_config_color_transparent\"/>\n        </shape>\n    </item>\n\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"@color/app_color_theme_1\"/>\n            </shape>\n        </clip>\n    </item>\n\n</layer-list>\n\n"
  },
  {
    "path": "qmuidemo/src/main/res/drawable-night/launcher_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/qmui_config_color_pure_black\"/>\n    <item android:drawable=\"@mipmap/about_logo\" android:gravity=\"center\"/>\n</layer-list>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/activity_arch_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\"\n                                                android:background=\"@color/qmui_config_color_white\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/activity_translucent.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\"\n              android:fitsSystemWindows=\"true\"\n              android:orientation=\"vertical\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fitsSystemWindows=\"true\">\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@color/qmui_config_color_white\"\n            android:gravity=\"center\"\n            android:padding=\"30dp\"\n            android:text=\"@string/statusBarHelper_translucentActivity_desc\"/>\n    </FrameLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/app_color_theme_4\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/drawablehelper_createfromview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\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              android:padding=\"20dp\">\n\n    <ImageView\n        android:id=\"@+id/createFromViewDisplay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:adjustViewBounds=\"true\"\n        android:contentDescription=\"截图当前界面显示\"\n        android:background=\"@color/qmui_config_color_gray_3\"\n        android:padding=\"1px\"/>\n\n</LinearLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@color/qmui_config_color_white\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fillViewport=\"true\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\"\n            android:paddingBottom=\"25dp\"\n            android:paddingTop=\"70dp\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:contentDescription=\"Logo\"\n                android:src=\"@mipmap/about_logo\"/>\n\n            <TextView\n                android:id=\"@+id/version\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"15dp\"\n                android:textColor=\"?attr/qmui_config_color_gray_3\"\n                android:textSize=\"16sp\"/>\n\n            <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView\n                android:id=\"@+id/about_list\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"35dp\"/>\n\n            <Space\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\"/>\n\n            <TextView\n                android:id=\"@+id/copyright\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"25dp\"\n                android:gravity=\"center_horizontal\"\n                android:textColor=\"?attr/qmui_config_color_gray_7\"/>\n\n        </LinearLayout>\n\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_animation_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.QMUIAnimationListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:divider=\"@null\"\n        android:dividerHeight=\"0dp\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_arch_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <Space\n            android:id=\"@+id/anchor\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            android:layout_centerInParent=\"true\"/>\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_above=\"@id/anchor\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"20dp\"\n            android:textColor=\"?attr/qmui_config_color_gray_1\"\n            android:textSize=\"32sp\"/>\n\n        <TextView\n            android:id=\"@+id/test\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_above=\"@id/title\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"20dp\"\n            android:textColor=\"?attr/qmui_config_color_gray_1\"\n            android:textSize=\"32sp\"\n            android:text=\"test for skin maker\"/>\n\n        <TextView\n            android:id=\"@+id/test_only_id\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_above=\"@id/test\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"20dp\"\n            android:textColor=\"?attr/qmui_config_color_gray_1\"\n            android:textSize=\"32sp\"\n            android:text=\"test for skin maker(id)\"/>\n\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/btn\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/anchor\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginLeft=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginRight=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center\"\n            android:paddingBottom=\"10dp\"\n            android:paddingLeft=\"16dp\"\n            android:paddingRight=\"16dp\"\n            android:paddingTop=\"10dp\"\n            android:text=\"\"\n            app:qmui_isRadiusAdjustBounds=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/btn_1\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/btn\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginLeft=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginRight=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center\"\n            android:paddingBottom=\"10dp\"\n            android:paddingLeft=\"16dp\"\n            android:paddingRight=\"16dp\"\n            android:paddingTop=\"10dp\"\n            android:text=\"popBackStack and then startActivity\"\n            app:qmui_isRadiusAdjustBounds=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/btn_2\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/btn_1\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginLeft=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginRight=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center\"\n            android:paddingBottom=\"10dp\"\n            android:paddingLeft=\"16dp\"\n            android:paddingRight=\"16dp\"\n            android:paddingTop=\"10dp\"\n            android:text=\"start nav fragment\"\n            app:qmui_isRadiusAdjustBounds=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/btn_3\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/btn_2\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginLeft=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginRight=\"?attr/qmui_content_spacing_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center\"\n            android:paddingBottom=\"10dp\"\n            android:paddingLeft=\"16dp\"\n            android:paddingRight=\"16dp\"\n            android:paddingTop=\"10dp\"\n            android:text=\"parentFragment.startFragment\"\n            app:qmui_isRadiusAdjustBounds=\"true\"/>\n    </RelativeLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:fitsSystemWindows=\"true\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\"\n            android:paddingLeft=\"@dimen/qmui_content_spacing_horizontal\"\n            android:paddingRight=\"@dimen/qmui_content_spacing_horizontal\">\n\n            <com.qmuiteam.qmui.layout.QMUIFrameLayout\n                style=\"@style/button_wrapper_style\">\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingBottom=\"10dp\"\n                    android:paddingLeft=\"16dp\"\n                    android:paddingRight=\"16dp\"\n                    android:paddingTop=\"10dp\"\n                    android:text=\"圆角为短边的一半\"\n                    app:qmui_isRadiusAdjustBounds=\"true\"\n                    android:layout_gravity=\"center\"/>\n\n            </com.qmuiteam.qmui.layout.QMUIFrameLayout>\n\n\n            <com.qmuiteam.qmui.layout.QMUIFrameLayout style=\"@style/button_wrapper_style\">\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:clickable=\"true\"\n                    android:gravity=\"center\"\n                    android:padding=\"10dp\"\n                    android:text=\"指定圆角大小\"\n                    app:qmui_radius=\"4dp\"/>\n\n            </com.qmuiteam.qmui.layout.QMUIFrameLayout>\n\n            <com.qmuiteam.qmui.layout.QMUIFrameLayout style=\"@style/button_wrapper_style\">\n\n                <FrameLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\">\n\n                    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                        android:layout_width=\"60dp\"\n                        android:layout_height=\"60dp\"\n                        android:gravity=\"center\"\n                        android:text=\"指\"\n                        app:qmui_radiusTopLeft=\"8dp\"/>\n\n                    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                        android:layout_width=\"60dp\"\n                        android:layout_height=\"60dp\"\n                        android:layout_marginLeft=\"64dp\"\n                        android:gravity=\"center\"\n                        android:text=\"定\"\n                        app:qmui_radiusTopRight=\"8dp\"/>\n\n                    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                        android:layout_width=\"60dp\"\n                        android:layout_height=\"60dp\"\n                        android:layout_marginTop=\"64dp\"\n                        android:gravity=\"center\"\n                        android:text=\"方\"\n                        app:qmui_radiusBottomLeft=\"8dp\"/>\n\n                    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                        android:layout_width=\"60dp\"\n                        android:layout_height=\"60dp\"\n                        android:layout_marginLeft=\"64dp\"\n                        android:layout_marginTop=\"64dp\"\n                        android:gravity=\"center\"\n                        android:text=\"向\"\n                        app:qmui_radiusBottomRight=\"8dp\"/>\n\n                </FrameLayout>\n\n            </com.qmuiteam.qmui.layout.QMUIFrameLayout>\n\n            <com.qmuiteam.qmui.layout.QMUILinearLayout style=\"@style/button_wrapper_style\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    style=\"@style/QDCommonTitle\"\n                    android:text=\"使用 ColorStateList 指定文字颜色、边框颜色、背景颜色及其点击态\" />\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:clickable=\"true\"\n                    android:gravity=\"center\"\n                    android:padding=\"10dp\"\n                    android:text=\"更改文字颜色、边框颜色和背景颜色\"\n                    android:textColor=\"@color/s_app_color_blue_to_red\"\n                    app:qmui_backgroundColor=\"@color/s_app_color_blue_3\"\n                    app:qmui_borderColor=\"@color/s_app_color_blue_to_red\"\n                    app:qmui_borderWidth=\"1px\"\n                    android:layout_gravity=\"center_horizontal\"\n                    app:qmui_skin_background=\"?attr/app_skin_btn_test_bg\"\n                    app:qmui_skin_border=\"?attr/app_skin_btn_test_border\"\n                    app:qmui_skin_text_color=\"?attr/app_skin_btn_test_border\"/>\n            </com.qmuiteam.qmui.layout.QMUILinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUILinearLayout style=\"@style/button_wrapper_style\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    style=\"@style/QDCommonTitle\"\n                    android:text=\"设置 setChangeAlphaWhenPressed(true) 在点击时改变整体 alpha\" />\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:id=\"@+id/alpha_button\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:clickable=\"true\"\n                    android:gravity=\"center\"\n                    android:padding=\"10dp\"\n                    android:text=\"changeAlphaWhenPressed\"\n                    android:textColor=\"@color/app_color_blue\"\n                    app:qmui_backgroundColor=\"@color/app_color_blue_3\"\n                    app:qmui_borderColor=\"@color/app_color_blue\"\n                    app:qmui_borderWidth=\"1px\"\n                    android:layout_gravity=\"center_horizontal\"\n                    app:qmui_skin_border=\"?attr/app_skin_btn_test_border_single\"\n                    app:qmui_skin_background=\"?attr/app_skin_btn_test_bg_single\"\n                    app:qmui_skin_text_color=\"?attr/app_skin_btn_test_border_single\"/>\n            </com.qmuiteam.qmui.layout.QMUILinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUILinearLayout style=\"@style/button_wrapper_style\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    style=\"@style/QDCommonTitle\"\n                    android:text=\"设置 setChangeAlphaWhenPressed(true) 在点击时改变整体 alpha\" />\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:id=\"@+id/test_java_kotlin_skin\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:clickable=\"true\"\n                    android:gravity=\"center\"\n                    android:padding=\"10dp\"\n                    android:text=\"通过 kotlin 设置 skin\"\n                    android:textColor=\"@color/app_color_blue\"\n                    app:qmui_backgroundColor=\"@color/app_color_blue_3\"\n                    app:qmui_borderColor=\"@color/app_color_blue\"\n                    app:qmui_borderWidth=\"1px\"\n                    android:layout_gravity=\"center_horizontal\"/>\n            </com.qmuiteam.qmui.layout.QMUILinearLayout>\n        </LinearLayout>\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_collapsing_topbar_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/qmui_config_color_white\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <com.qmuiteam.qmui.widget.QMUIAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"256dp\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.qmuiteam.qmui.widget.QMUICollapsingTopBarLayout\n            android:id=\"@+id/collapsing_topbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:qmui_collapsedTitleGravity=\"center\"\n            app:qmui_contentScrim=\"?attr/qmui_config_color_blue\"\n            app:qmui_expandedTitleGravity=\"center_horizontal|bottom\"\n            app:qmui_expandedTitleMarginBottom=\"20dp\"\n            app:qmui_statusBarScrim=\"?attr/qmui_config_color_blue\"\n            app:qmui_followTopBarCommonSkin=\"true\"\n            android:minHeight=\"?attr/qmui_topbar_height\">\n\n            <ImageView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:contentDescription=\"@string/common_example\"\n                android:fitsSystemWindows=\"false\"\n                android:scaleType=\"centerCrop\"\n                android:src=\"@mipmap/qd_show_img_1\"\n                app:qmui_layout_collapseMode=\"parallax\"\n                app:qmui_layout_collapseParallaxMultiplier=\"0.7\"/>\n\n            <com.qmuiteam.qmui.widget.QMUITopBar\n                android:id=\"@+id/topbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/qmui_topbar_height\"\n                android:fitsSystemWindows=\"true\"\n                app:qmui_layout_collapseMode=\"pin\"\n                android:background=\"@color/qmui_config_color_transparent\"\n                app:qmui_bottomDividerHeight=\"0px\"/>\n        </com.qmuiteam.qmui.widget.QMUICollapsingTopBarLayout>\n    </com.qmuiteam.qmui.widget.QMUIAppBarLayout>\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:scrollbars=\"none\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"/>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_colorhelper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <!-- 设置颜色的 alpha 值 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/colorHelper_alpha_title\"/>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/colorHelper_column_marginBottom\"\n                android:orientation=\"horizontal\">\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\"\n                    android:orientation=\"vertical\">\n\n                    <View\n                        android:layout_width=\"@dimen/colorHelper_square_length\"\n                        android:layout_height=\"@dimen/colorHelper_square_length\"\n                        android:background=\"@color/colorHelper_square_alpha_background\"/>\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"@dimen/colorHelper_square_desc_marginTop\"\n                        android:text=\"@string/colorHelper_squqre_origin\"/>\n\n                </LinearLayout>\n\n                <ImageView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_margin=\"@dimen/colorHelper_square_margin\"\n                    android:contentDescription=\"@null\"\n                    android:src=\"@mipmap/arrowright\"/>\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\"\n                    android:orientation=\"vertical\">\n\n                    <View\n                        android:id=\"@+id/square_alpha\"\n                        android:layout_width=\"@dimen/colorHelper_square_length\"\n                        android:layout_height=\"@dimen/colorHelper_square_length\"/>\n\n                    <TextView\n                        android:id=\"@+id/square_desc_alpha\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"@dimen/colorHelper_square_desc_marginTop\"/>\n\n                </LinearLayout>\n            </LinearLayout>\n\n            <!-- 根据比例，在两个 color 值之间计算出一个 color 值 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/colorHelper_ratio_title\"/>\n\n            <LinearLayout\n                android:id=\"@+id/ratioSeekBarWrap\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/colorHelper_column_marginBottom\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <View\n                    android:layout_width=\"@dimen/colorHelper_square_length\"\n                    android:layout_height=\"@dimen/colorHelper_square_length\"\n                    android:background=\"@color/colorHelper_square_from_ratio_background\"/>\n\n                <SeekBar\n                    android:id=\"@+id/ratioSeekBar\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:max=\"100\"\n                    android:paddingLeft=\"20dp\"\n                    android:paddingRight=\"20dp\"/>\n\n                <View\n                    android:layout_width=\"@dimen/colorHelper_square_length\"\n                    android:layout_height=\"@dimen/colorHelper_square_length\"\n                    android:background=\"@color/colorHelper_square_to_ratio_background\"/>\n\n            </LinearLayout>\n\n            <!-- 将 color 颜色值转换为字符串 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/colorHelper_transform_title\"/>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/colorHelper_column_marginBottom\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:id=\"@+id/transformTextView\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:textColor=\"@color/app_color_blue\"/>\n\n            </LinearLayout>\n\n\n        </LinearLayout>\n    </ScrollView>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_continuous_nested_scroll.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n    <!-- must consume the fitsSystemWindows -->\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fitsSystemWindows=\"true\">\n        <com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout\n            android:id=\"@+id/pull_to_refresh\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n            <com.qmuiteam.qmui.nestedScroll.QMUIContinuousNestedScrollLayout\n                android:id=\"@+id/coordinator\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"/>\n        </com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout>\n    </FrameLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_drawablehelper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <!-- 创建一张指定大小的纯色图片，支持圆角 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/drawableHelper_solid_image_title\"/>\n\n            <ImageView\n                android:id=\"@+id/solidImage\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/drawableHelper_column_marginBottom\"\n                android:contentDescription=\"@string/common_example\"/>\n\n            <!-- 创建一张圆形渐变图片，支持圆角 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/drawableHelper_circle_gradient_title\"/>\n\n            <ImageView\n                android:id=\"@+id/circleGradient\"\n                android:layout_width=\"@dimen/drawableHelper_common_shape_size\"\n                android:layout_height=\"@dimen/drawableHelper_common_shape_size\"\n                android:layout_marginBottom=\"@dimen/drawableHelper_column_marginBottom\"\n                android:contentDescription=\"@string/common_example\"/>\n\n            <!-- 设置 Drawable 的颜色 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/drawableHelper_tint_color_title\"/>\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/drawableHelper_column_marginBottom\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <ImageView\n                    android:id=\"@+id/tintColorOrigin\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:contentDescription=\"@string/common_example\"/>\n\n                <ImageView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/colorHelper_square_margin\"\n                    android:contentDescription=\"@null\"\n                    android:src=\"@mipmap/arrowright\"/>\n\n                <ImageView\n                    android:id=\"@+id/tintColor\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/colorHelper_square_margin\"\n                    android:contentDescription=\"@string/common_example\"/>\n\n            </LinearLayout>\n\n            <!-- 创建带上分隔线或下分隔线的 Drawable -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/drawableHelper_separator_title\"/>\n\n            <View\n                android:id=\"@+id/separator\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/drawableHelper_common_shape_size\"\n                android:layout_marginBottom=\"@dimen/drawableHelper_column_marginBottom\"/>\n\n            <!-- 从一个 View 创建 Bitmap -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/drawableHelper_create_from_view_title\"/>\n\n            <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                android:id=\"@+id/createFromView\"\n                style=\"@style/QDRoundButtonStyle\"\n                android:text=\"@string/drawableHelper_create_from_view_button\"/>\n\n        </LinearLayout>\n    </ScrollView>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_emptyview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.qmuiteam.qmui.widget.QMUIEmptyView\n        android:id=\"@+id/emptyView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        app:qmui_title_text=\"@string/emptyView_mode_desc_double\"\n        app:qmui_detail_text=\"@string/emptyView_mode_desc_detail_double\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_floatlayout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:fitsSystemWindows=\"true\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/common_content_spacing\">\n\n            <com.qmuiteam.qmui.widget.QMUIFloatLayout\n                android:id=\"@+id/qmuidemo_floatlayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"left\"\n                app:qmui_childHorizontalSpacing=\"20dp\"\n                app:qmui_childVerticalSpacing=\"20dp\"/>\n        </LinearLayout>\n\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_fsw_viewpager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.layout.QMUIFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\"\n              android:background=\"?attr/app_primary_color\">\n    <com.qmuiteam.qmui.widget.QMUIViewPager\n        android:id=\"@+id/pager\"\n        android:background=\"?attr/app_content_bg_color\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"@dimen/home_tab_height\"\n        android:fitsSystemWindows=\"true\"/>\n    <com.qmuiteam.qmui.widget.tab.QMUITabSegment\n        android:id=\"@+id/tabs\"\n        android:layout_gravity=\"bottom\"\n        android:textSize=\"12sp\"\n        app:qmui_tab_icon_position=\"top\"\n        app:qmui_tab_selected_text_size=\"16sp\"\n        app:qmui_tab_normal_text_size=\"14sp\"\n        app:qmui_topDividerHeight=\"1px\"\n        app:qmui_topDividerColor=\"?attr/qmui_skin_support_color_separator\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/home_tab_height\"/>\n</com.qmuiteam.qmui.layout.QMUIFrameLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_grouplistview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_background\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background_1\">\n\n        <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView\n            android:id=\"@+id/groupListView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\" />\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/app_content_bg_color\"\n    android:clipChildren=\"false\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <com.qmuiteam.qmui.widget.QMUIViewPager\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"@dimen/home_tab_height\" />\n\n    <com.qmuiteam.qmui.widget.tab.QMUITabSegment\n        android:id=\"@+id/tabs\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/home_tab_height\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:textSize=\"12sp\"\n        app:qmui_tab_icon_position=\"top\"\n        app:qmui_topDividerColor=\"?attr/qmui_skin_support_color_separator\"\n        app:qmui_topDividerHeight=\"1px\" />\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\"\n                                                android:background=\"?attr/qmui_skin_support_color_background\">\n    <com.qmuiteam.qmuidemo.view.QDShadowAdjustLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.qmuiteam.qmui.layout.QMUILinearLayout\n            android:id=\"@id/layout_for_test\"\n            android:layout_width=\"260dp\"\n            android:layout_height=\"260dp\"\n            android:background=\"@color/qmui_config_color_white\"\n            android:gravity=\"center\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"40dp\"\n            android:orientation=\"vertical\"\n            app:qmui_outerNormalColor=\"?attr/qmui_skin_support_color_background\"\n            app:qmui_borderColor=\"?attr/qmui_skin_support_color_separator\"\n            app:qmui_showBorderOnlyBeforeL=\"true\">\n\n            <TextView\n                android:id=\"@+id/alpha_tv\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"/>\n\n            <TextView\n                android:id=\"@+id/elevation_tv\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"/>\n\n        </com.qmuiteam.qmui.layout.QMUILinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom\"\n            android:orientation=\"vertical\"\n            android:paddingLeft=\"@dimen/common_content_spacing\"\n            android:paddingRight=\"@dimen/common_content_spacing\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginRight=\"10dp\"\n                    android:text=\"alpha\"/>\n\n                <SeekBar\n                    android:id=\"@+id/test_seekbar_alpha\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:max=\"100\"\n                    android:progress=\"0\"/>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginRight=\"10dp\"\n                    android:text=\"elevation\"/>\n\n                <SeekBar\n                    android:id=\"@+id/test_seekbar_elevation\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:max=\"100\"\n                    android:progress=\"0\"/>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\"\n                android:gravity=\"center_vertical\">\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginRight=\"10dp\"\n                    android:text=\"shadowColor(>= android 9)\"/>\n                <Button\n                    android:id=\"@+id/shadow_color_red\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"#ff0000\"\n                    android:textColor=\"#ff0000\"\n                    android:layout_marginRight=\"10dp\"\n                    android:paddingTop=\"4dp\"\n                    android:paddingBottom=\"4dp\"\n                    android:paddingLeft=\"10dp\"\n                    android:paddingRight=\"10dp\"/>\n                <Button\n                    android:id=\"@+id/shadow_color_blue\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"#0000ff\"\n                    android:textColor=\"#0000ff\"\n                    android:paddingTop=\"4dp\"\n                    android:paddingBottom=\"4dp\"\n                    android:paddingLeft=\"10dp\"\n                    android:paddingRight=\"10dp\"/>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\"\n                android:gravity=\"center_vertical\">\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginRight=\"10dp\"\n                    android:text=\"radius\"/>\n                <Button\n                    android:id=\"@+id/radius_15dp\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"15dp\"\n                    android:layout_marginRight=\"10dp\"\n                    android:paddingTop=\"4dp\"\n                    android:paddingBottom=\"4dp\"\n                    android:paddingLeft=\"10dp\"\n                    android:paddingRight=\"10dp\"/>\n                <Button\n                    android:id=\"@+id/radius_half_width\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"half_width\"\n                    android:paddingTop=\"4dp\"\n                    android:paddingBottom=\"4dp\"\n                    android:paddingLeft=\"10dp\"\n                    android:paddingRight=\"10dp\"/>\n                <Button\n                    android:id=\"@+id/radius_half_height\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"half_height\"\n                    android:paddingTop=\"4dp\"\n                    android:paddingBottom=\"4dp\"\n                    android:paddingLeft=\"10dp\"\n                    android:paddingRight=\"10dp\"/>\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:text=\"配置取消一个边的圆角（阴影会丢失）\"/>\n            <RadioGroup\n                android:id=\"@+id/hide_radius_group\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <RadioButton\n                    android:id=\"@+id/hide_radius_none\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"40dp\"\n                    android:textSize=\"12sp\"\n                    android:text=\"none\"\n                    android:gravity=\"center\"/>\n\n                <RadioButton\n                    android:id=\"@+id/hide_radius_top\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"40dp\"\n                    android:textSize=\"12sp\"\n                    android:text=\"top\"\n                    android:background=\"@null\"/>\n\n                <RadioButton\n                    android:id=\"@+id/hide_radius_right\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"40dp\"\n                    android:textSize=\"12sp\"\n                    android:text=\"right\"\n                    android:background=\"@null\"/>\n\n                <RadioButton\n                    android:id=\"@+id/hide_radius_bottom\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"40dp\"\n                    android:textSize=\"12sp\"\n                    android:text=\"bottom\"\n                    android:background=\"@null\"/>\n\n                <RadioButton\n                    android:id=\"@+id/hide_radius_left\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"40dp\"\n                    android:textSize=\"12sp\"\n                    android:text=\"left\"\n                    android:background=\"@null\"/>\n            </RadioGroup>\n        </LinearLayout>\n    </com.qmuiteam.qmuidemo.view.QDShadowAdjustLayout>\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_link_texview_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:orientation=\"vertical\"\n        android:padding=\"25dp\">\n\n        <TextView\n            style=\"@style/QDCommonTitle\"\n            android:ellipsize=\"end\"\n            android:text=\"@string/linkTextView_auto_identify\"/>\n\n        <com.qmuiteam.qmui.widget.textview.QMUILinkTextView\n            android:id=\"@+id/link_text_view\"\n            style=\"@style/QDCommonDescription\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"20dp\"\n            android:gravity=\"left\"\n            android:text=\"@string/linkTextView_auto_identify_word\"\n            app:qmui_linkBackgroundColor=\"@color/s_app_color_gray\"\n            app:qmui_linkTextColor=\"@color/s_app_color_blue_2\"/>\n\n    </LinearLayout>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"?attr/app_content_bg_color\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:divider=\"@null\"\n        android:dividerHeight=\"0dp\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_loop_viewpager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\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/app_primary_color\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBar\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/qmui_topbar_height\"/>\n\n    <com.qmuiteam.qmui.widget.QMUIViewPager\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"?attr/qmui_skin_support_color_background\"/>\n</FrameLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_notch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.layout.QMUIFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@color/qmui_config_color_white\">\n\n    <com.qmuiteam.qmui.widget.QMUINotchConsumeLayout\n        android:id=\"@+id/not_safe_bg\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <TextView\n            android:id=\"@+id/safe_area_tv\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"?attr/qmui_config_color_gray_2\"\n            android:fitsSystemWindows=\"false\"\n            android:gravity=\"center_horizontal\"\n            android:text=\"安全区域\\nTODO vivo 显示与亮度-第三方应用显示比例\\nTODO OPPO 设置-显示-应用全屏显示-凹形区域显示控制\"\n            android:textColor=\"@color/qmui_config_color_white\"\n            android:textSize=\"16sp\"/>\n    </com.qmuiteam.qmui.widget.QMUINotchConsumeLayout>\n\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <FrameLayout\n        android:id=\"@+id/tabs_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.qmuiteam.qmui.widget.tab.QMUITabSegment\n            android:id=\"@+id/tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/home_tab_height\"\n            app:qmui_topDividerHeight=\"1px\"\n            app:qmui_topDividerColor=\"?attr/qmui_skin_support_color_separator\"\n            android:textSize=\"12sp\"/>\n    </FrameLayout>\n\n</com.qmuiteam.qmui.layout.QMUIFrameLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_pagerlayoutmanager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <FrameLayout\n        android:id=\"@+id/pagerWrap\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_popup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/qmui_config_color_white\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <com.qmuiteam.qmui.widget.QMUIFloatLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:qmui_childHorizontalSpacing=\"20dp\"\n        app:qmui_childVerticalSpacing=\"20dp\"\n        android:padding=\"20dp\">\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn1\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/popup_normal_action_button_text_show\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn2\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/popup_list_action_button_text_show\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn3\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/popup_dim_amount_action_button_text_show\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn4\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/popup_dynamic_update_content\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn5\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/fullscreen_popup_with_close_btn\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn6\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/fullscreen_popup_with_input\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn7\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/quick_action\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actionBtn8\"\n            style=\"@style/QDRoundButtonStyle\"\n            android:text=\"@string/quick_action_more\" />\n\n    </com.qmuiteam.qmui.widget.QMUIFloatLayout>\n\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_priority_linear_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\"\n                                                android:background=\"@color/qmui_config_color_white\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fillViewport=\"true\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:paddingBottom=\"10dp\"\n            android:paddingLeft=\"10dp\"\n            android:paddingRight=\"10dp\"\n            android:paddingTop=\"10dp\">\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"内容超出，压缩每一个child\"/>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"提高某些 child 的权重，不被压缩\"/>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"1. 这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"2. 测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"3. 测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"4. 测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n\n            <com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"5. 测试文案测试文案测试\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"测试文案测试文案测试\"\n                    app:qmui_layout_priority=\"disposable\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"4dp\"\n                    android:layout_marginRight=\"4dp\"\n                    android:ellipsize=\"end\"\n                    android:singleLine=\"true\"\n                    android:text=\"这个元素将不被压缩\"\n                    app:qmui_layout_priority=\"incompressible\"/>\n            </com.qmuiteam.qmui.layout.QMUIPriorityLinearLayout>\n        </LinearLayout>\n\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_progressbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/common_content_spacing\">\n\n            <com.qmuiteam.qmui.widget.QMUIProgressBar\n                android:id=\"@+id/rectProgressBar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"24dp\"\n                android:textColor=\"@color/qmui_config_color_white\"\n                android:textSize=\"16sp\"\n                app:qmui_background_color=\"@color/qmui_config_color_gray_8\"\n                app:qmui_progress_color=\"@color/app_color_blue_2\"\n                app:qmui_type=\"type_rect\"\n                app:qmui_skin_background=\"?attr/app_skin_progress_bar_bg_color\"\n                app:qmui_skin_progress_color=\"?attr/app_skin_progress_bar_progress_color\"\n                app:qmui_skin_text_color=\"?attr/app_skin_progress_bar_text_color\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIProgressBar\n                android:id=\"@+id/circleProgressBar\"\n                android:layout_width=\"250dp\"\n                android:layout_height=\"250dp\"\n                android:layout_marginTop=\"30dp\"\n                android:textColor=\"?attr/qmui_config_color_gray_4\"\n                android:textSize=\"22sp\"\n                app:qmui_background_color=\"?attr/qmui_config_color_gray_8\"\n                app:qmui_progress_color=\"@color/app_color_blue_2\"\n                app:qmui_stroke_width=\"18dp\"\n                app:qmui_type=\"type_circle\"\n                app:qmui_skin_background=\"?attr/app_skin_progress_bar_bg_color\"\n                app:qmui_skin_progress_color=\"?attr/app_skin_progress_bar_progress_color\"\n                app:qmui_skin_text_color=\"?attr/app_skin_progress_bar_text_color\"/>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"50dp\"\n                android:orientation=\"horizontal\">\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:id=\"@+id/startBtn\"\n                    style=\"@style/QDRoundButtonStyle\"\n                    android:layout_width=\"0dp\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/progress_start_button_text\" />\n\n                <Space\n                    android:layout_width=\"@dimen/qmui_content_padding_horizontal\"\n                    android:layout_height=\"0dp\" />\n\n                <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                    android:id=\"@+id/backBtn\"\n                    style=\"@style/QDRoundButtonStyle\"\n                    android:layout_width=\"0dp\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/progress_back_button_text\" />\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_pull_horizontal_test_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout\n        android:id=\"@+id/pull_layout\"\n        app:layout_constraintTop_toBottomOf=\"@+id/topbar\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"?attr/qmui_skin_support_color_background\">\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/qmui_config_color_white\"\n            app:qmui_is_target=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIVerticalTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"这是左边ActionView\"\n            android:textSize=\"16dp\"\n            android:textColor=\"@color/qmui_config_color_pure_black\"\n            app:qmui_action_view_init_offset=\"-20dp\"\n            app:qmui_pull_edge=\"left\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIVerticalTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"这是右边ActionView\"\n            android:textSize=\"16dp\"\n            android:textColor=\"@color/qmui_config_color_pure_black\"\n            app:qmui_action_view_init_offset=\"-20dp\"\n            app:qmui_pull_edge=\"right\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"true\"/>\n    </com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_pull_refresh_and_load_more_test_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout\n        android:id=\"@+id/pull_layout\"\n        app:layout_constraintTop_toBottomOf=\"@+id/topbar\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"?attr/qmui_skin_support_color_background\">\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:qmui_is_target=\"true\"/>\n\n        <com.qmuiteam.qmui.widget.pullLayout.QMUIPullRefreshView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            app:qmui_action_view_init_offset=\"-20dp\"\n            app:qmui_pull_edge=\"top\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"true\"\n            app:qmui_need_receive_fling_from_target_view=\"false\"/>\n\n        <com.qmuiteam.qmui.widget.pullLayout.QMUIPullLoadMoreView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:qmui_action_view_init_offset=\"0dp\"\n            app:qmui_pull_edge=\"bottom\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"true\"\n            app:qmui_need_receive_fling_from_target_view=\"true\"/>\n    </com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_pull_refresh_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"?attr/qmui_skin_support_color_background\"\n        android:fitsSystemWindows=\"true\">\n        <com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout\n            android:id=\"@+id/pull_to_refresh\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/listview\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:background=\"@color/qmui_config_color_white\"/>\n        </com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout>\n    </FrameLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_pull_vertical_test_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout\n        android:id=\"@+id/pull_layout\"\n        app:layout_constraintTop_toBottomOf=\"@+id/topbar\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"?attr/qmui_skin_support_color_background\">\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/qmui_config_color_white\"\n            app:qmui_is_target=\"true\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"这是上边ActionView\"\n            android:textSize=\"16dp\"\n            android:textColor=\"@color/qmui_config_color_pure_black\"\n            app:qmui_action_view_init_offset=\"-20dp\"\n            app:qmui_pull_edge=\"top\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"true\"\n            app:qmui_need_receive_fling_from_target_view=\"false\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"这是下边ActionView\"\n            android:textSize=\"16dp\"\n            android:textColor=\"@color/qmui_config_color_pure_black\"\n            app:qmui_action_view_init_offset=\"-20dp\"\n            app:qmui_pull_edge=\"bottom\"\n            app:qmui_target_view_trigger_offset=\"wrap\"\n            app:qmui_pull_rate=\"0.45\"\n            app:qmui_can_over_pull=\"false\"\n            app:qmui_need_receive_fling_from_target_view=\"true\"/>\n    </com.qmuiteam.qmui.widget.pullLayout.QMUIPullLayout>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_qqface_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"单行滚动\"/>\n\n            <com.qmuiteam.qmui.type.view.MarqueeTypeView\n                android:id=\"@+id/marquee1\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"/>\n\n            <com.qmuiteam.qmui.type.view.MarqueeTypeView\n                android:id=\"@+id/marquee2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"10dp\"/>\n\n            <com.qmuiteam.qmuidemo.fragment.components.qqface.Test\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"/>\n\n            <com.qmuiteam.qmui.type.view.LineTypeView\n                android:id=\"@+id/line_type_1\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"新排版, 序号对齐\"\n                android:layout_marginTop=\"20dp\"/>\n\n            <com.qmuiteam.qmui.type.view.LineTypeView\n                android:id=\"@+id/line_type_2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"藏文\"\n                android:layout_marginTop=\"20dp\"/>\n\n            <com.qmuiteam.qmui.type.view.LineTypeView\n                android:id=\"@+id/line_type_3\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"单行表情，末尾省略\"\n                android:layout_marginTop=\"20dp\"/>\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface1\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:singleLine=\"true\"\n                android:ellipsize=\"end\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"最多显示3行，末尾省略\"/>\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"3\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:ellipsize=\"end\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"单行，开端省略\"/>\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface3\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:singleLine=\"true\"\n                android:ellipsize=\"start\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"最多显示3行，开端省略\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface4\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:ellipsize=\"start\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"单行，中间省略\"/>\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface5\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:singleLine=\"true\"\n                android:ellipsize=\"middle\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"最多显示3行，中间省略\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface6\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:ellipsize=\"middle\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"全是表情也能优雅的省略\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface7\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:singleLine=\"true\"\n                android:ellipsize=\"start\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface8\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:singleLine=\"true\"\n                android:layout_marginTop=\"10dp\"\n                android:ellipsize=\"middle\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface9\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:layout_marginTop=\"10dp\"\n                android:singleLine=\"true\"\n                android:ellipsize=\"end\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface10\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:layout_marginTop=\"10dp\"\n                android:maxLines=\"3\"\n                android:ellipsize=\"start\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface11\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:layout_marginTop=\"10dp\"\n                android:ellipsize=\"middle\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface12\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:layout_marginTop=\"10dp\"\n                android:ellipsize=\"end\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface13\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:textSize=\"20sp\"\n                android:layout_marginTop=\"10dp\"\n                android:ellipsize=\"end\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"QMUITouchSpan\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface14\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:layout_marginTop=\"10dp\" />\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface15\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:layout_marginTop=\"10dp\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface16\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:maxLines=\"3\"\n                android:layout_marginTop=\"10dp\"\n                app:qmui_skin_underline=\"?attr/app_skin_btn_test_border\"\n                app:qmui_skin_more_text_color=\"?attr/app_skin_btn_test_border\"\n                app:qmui_skin_more_bg_color=\"?attr/app_skin_common_background_1\"/>\n\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"更多\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface17\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:layout_marginTop=\"10dp\"\n                android:maxLines=\"3\"\n                android:ellipsize=\"end\"\n                app:qmui_more_action_text=\"更多\"\n                app:qmui_more_action_color=\"@color/s_app_color_blue_2\"\n                app:qmui_more_action_bg_color=\"@color/s_app_color_gray\"\n                app:qmui_skin_underline=\"?attr/app_skin_btn_test_border\"\n                app:qmui_skin_more_text_color=\"?attr/app_skin_btn_test_border\"\n                app:qmui_skin_more_bg_color=\"?attr/app_skin_common_background_1\"/>\n\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"段间距\"/>\n\n            <com.qmuiteam.qmui.qqface.QMUIQQFaceView\n                android:id=\"@+id/qqface18\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:layout_marginTop=\"10dp\"/>\n        </LinearLayout>\n    </ScrollView>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_radius_imageview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/circularImageView_item_xml_circle_title\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_is_circle=\"true\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"50dp\"\n                android:text=\"@string/circularImageView_item_xml_oval_title\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_is_oval=\"true\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"50dp\"\n                android:text=\"@string/circularImageView_item_xml_round_rect_title\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_corner_radius=\"15dp\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"50dp\"\n                android:text=\"@string/circularImageView_modify_tip\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n                android:id=\"@+id/radiusImageView\"\n                android:layout_width=\"300dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_corner_radius=\"15dp\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n        </LinearLayout>\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_radius_imageview2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/circularImageView_item_xml_circle_title\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView2\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                android:scaleType=\"centerCrop\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_is_circle=\"true\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"50dp\"\n                android:text=\"@string/circularImageView_item_xml_round_rect_title\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView2\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                android:scaleType=\"centerCrop\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_corner_radius=\"15dp\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"50dp\"\n                android:text=\"@string/circularImageView_modify_tip\"/>\n\n            <com.qmuiteam.qmui.widget.QMUIRadiusImageView2\n                android:id=\"@+id/radiusImageView\"\n                android:layout_width=\"300dp\"\n                android:layout_height=\"wrap_content\"\n                android:clickable=\"true\"\n                android:src=\"@mipmap/example_image6\"\n                android:scaleType=\"centerCrop\"\n                app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n                app:qmui_border_width=\"1px\"\n                app:qmui_corner_radius=\"15dp\"\n                app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n                app:qmui_selected_border_width=\"1px\"\n                app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"/>\n\n        </LinearLayout>\n    </ScrollView>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_radius_imageview2_scale_type.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fitsSystemWindows=\"true\"\n        android:orientation=\"vertical\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <TextView\n            style=\"@style/QDCommonTitle\"\n            android:padding=\"20dp\"\n            android:text=\"@string/circularImageView_item_xml_scale_type_desc\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIRadiusImageView2\n            android:id=\"@+id/image_1\"\n            android:layout_width=\"220dp\"\n            android:layout_height=\"180dp\"\n            android:background=\"@color/qmui_config_color_pure_black\"\n            android:clickable=\"true\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@mipmap/ic_launcher\"\n            app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n            app:qmui_border_width=\"1px\"\n            app:qmui_corner_radius=\"60dp\"\n            app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n            app:qmui_selected_border_width=\"1px\"\n            app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"\n            android:layout_marginLeft=\"20dp\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIRadiusImageView2\n            android:id=\"@+id/image_2\"\n            android:layout_width=\"220dp\"\n            android:layout_height=\"180dp\"\n            android:layout_marginTop=\"20dp\"\n            android:background=\"@color/qmui_config_color_pure_black\"\n            android:clickable=\"true\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@mipmap/example_image6\"\n            app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n            app:qmui_border_width=\"1px\"\n            app:qmui_corner_radius=\"60dp\"\n            app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n            app:qmui_selected_border_width=\"1px\"\n            app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"\n            android:layout_marginLeft=\"20dp\"/>\n\n    </LinearLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_radius_imageview_scale_type.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <TextView\n            style=\"@style/QDCommonTitle\"\n            android:padding=\"20dp\"\n            android:text=\"@string/circularImageView_item_xml_scale_type_desc\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n            android:id=\"@+id/image_1\"\n            android:layout_width=\"220dp\"\n            android:layout_height=\"180dp\"\n            android:background=\"@color/qmui_config_color_pure_black\"\n            android:clickable=\"true\"\n            android:src=\"@mipmap/ic_launcher\"\n            app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n            app:qmui_border_width=\"1px\"\n            app:qmui_corner_radius=\"60dp\"\n            app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n            app:qmui_selected_border_width=\"1px\"\n            app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"\n            android:layout_marginLeft=\"20dp\"/>\n\n        <com.qmuiteam.qmui.widget.QMUIRadiusImageView\n            android:id=\"@+id/image_2\"\n            android:layout_width=\"220dp\"\n            android:layout_height=\"180dp\"\n            android:layout_marginTop=\"20dp\"\n            android:background=\"@color/qmui_config_color_pure_black\"\n            android:clickable=\"true\"\n            android:src=\"@mipmap/example_image6\"\n            app:qmui_border_color=\"?attr/qmui_config_color_gray_6\"\n            app:qmui_border_width=\"1px\"\n            app:qmui_corner_radius=\"60dp\"\n            app:qmui_selected_border_color=\"?attr/qmui_config_color_gray_4\"\n            app:qmui_selected_border_width=\"1px\"\n            app:qmui_selected_mask_color=\"?attr/qmui_config_color_gray_8\"\n            android:layout_marginLeft=\"20dp\"/>\n\n    </LinearLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_scheme.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/app_skin_common_background\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:orientation=\"vertical\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        android:padding=\"40dp\">\n\n        <TextView\n            style=\"@style/QDCommonTitle\"\n            android:text=\"请输入 scheme: \" />\n\n        <EditText\n            android:id=\"@+id/edit_text\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:background=\"@drawable/qmui_divider_bottom_bitmap\"\n            android:hint=\"qmui://xxx\"\n            android:paddingBottom=\"6dp\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/app_color_description\"\n            android:textSize=\"16sp\" />\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/button\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center\"\n            android:padding=\"10dp\"\n            android:text=\"点击跳转\"\n            android:textColor=\"@color/app_color_blue\"\n            app:qmui_backgroundColor=\"@color/app_color_blue_3\"\n            app:qmui_borderColor=\"@color/app_color_blue\"\n            app:qmui_borderWidth=\"1px\"\n            app:qmui_skin_background=\"?attr/app_skin_btn_test_bg_single\"\n            app:qmui_skin_border=\"?attr/app_skin_btn_test_border_single\"\n            app:qmui_skin_text_color=\"?attr/app_skin_btn_test_border_single\" />\n    </LinearLayout>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_section_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                                android:layout_width=\"match_parent\"\n                                                android:layout_height=\"match_parent\"\n                                                android:background=\"@color/qmui_config_color_white\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"?attr/qmui_skin_support_color_background\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout\n            android:id=\"@+id/pull_to_refresh\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <com.qmuiteam.qmui.widget.section.QMUIStickySectionLayout\n                android:id=\"@+id/section_layout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"/>\n        </com.qmuiteam.qmui.widget.pullRefreshLayout.QMUIPullRefreshLayout>\n    </FrameLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_slider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"vertical\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:paddingLeft=\"@dimen/qmui_content_spacing_horizontal\"\n        android:paddingRight=\"@dimen/qmui_content_spacing_horizontal\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.qmuiteam.qmui.widget.QMUISlider\n            android:id=\"@+id/slider\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginRight=\"20dp\"\n            android:layout_marginTop=\"20dp\"/>\n\n\n        <com.qmuiteam.qmui.widget.QMUISeekBar\n            android:id=\"@+id/seekBar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginRight=\"20dp\"\n            android:layout_marginTop=\"40dp\"/>\n    </LinearLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_spanhelper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:id=\"@+id/contentWrap\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <!-- 支持垂直居中的 ImageSpan -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanUtils_align_middle_title\"/>\n\n            <TextView\n                android:id=\"@+id/alignMiddle\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/spanUtils_column_marginBottom\"\n                android:lineSpacingExtra=\"6dp\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:textSize=\"@dimen/spanUtils_common_textSize\"/>\n\n            <!-- 支持增加左右间距的 ImageSpan -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanUtils_margin_image_title\"/>\n\n            <TextView\n                android:id=\"@+id/marginImage\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/spanUtils_column_marginBottom\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:textSize=\"@dimen/spanUtils_common_textSize\"/>\n\n            <!-- 整行的空白 Span，可用来用于制作段间距 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanUtils_block_space_title\"/>\n\n            <TextView\n                android:id=\"@+id/blockSpace\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/spanUtils_column_marginBottom\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:textSize=\"@dimen/spanUtils_common_textSize\"/>\n\n            <!-- 自定义部分文字的字体 -->\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanUtils_custom_type_face_title\"/>\n\n            <TextView\n                android:id=\"@+id/customTypeface\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:lineSpacingExtra=\"6dp\"\n                android:textColor=\"?attr/qmui_config_color_gray_5\"\n                android:textSize=\"@dimen/spanUtils_common_textSize\"/>\n\n        </LinearLayout>\n\n    </ScrollView>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_surface_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n    </FrameLayout>\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_swipe_delete_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:divider=\"@null\"\n        android:dividerHeight=\"0dp\"/>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_tab_viewpager2_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        android:orientation=\"vertical\"\n        app:qmui_skin_background=\"attr/app_skin_common_background\">\n\n        <com.qmuiteam.qmui.widget.tab.QMUITabSegment2\n            android:id=\"@+id/tabSegment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"40dp\"\n            android:background=\"@color/qmui_config_color_white\"\n            app:qmui_bottomDividerColor=\"?attr/qmui_skin_support_color_separator\"\n            app:qmui_bottomDividerHeight=\"1px\" />\n\n        <androidx.viewpager2.widget.ViewPager2\n            android:id=\"@+id/contentViewPager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\"\n            android:fitsSystemWindows=\"true\" />\n\n\n    </LinearLayout>\n\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\" />\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_tab_viewpager_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/qmui_topbar_height\"\n        android:background=\"@color/qmui_config_color_white\"\n        android:fitsSystemWindows=\"true\"\n        android:orientation=\"vertical\"\n        app:qmui_skin_background=\"attr/app_skin_common_background\">\n\n        <com.qmuiteam.qmui.widget.tab.QMUITabSegment\n            android:id=\"@+id/tabSegment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"40dp\"\n            android:background=\"@color/qmui_config_color_white\"\n            app:qmui_bottomDividerColor=\"?attr/qmui_skin_support_color_separator\"\n            app:qmui_bottomDividerHeight=\"1px\" />\n\n        <androidx.viewpager.widget.ViewPager\n            android:id=\"@+id/contentViewPager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\"\n            android:fitsSystemWindows=\"true\" />\n\n\n    </LinearLayout>\n\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\" />\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_touch_span_fix_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:padding=\"25dp\">\n\n            <TextView\n                style=\"@style/QDCommonDescription\"\n                android:text=\"@string/spanTouchFix_description\"\n                android:gravity=\"left\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"10dp\"\n                android:text=\"@string/spanTouchFix_system_behavior\"/>\n\n            <TextView\n                android:id=\"@+id/sysytem_tv_1\"\n                style=\"@style/QDCommonDescription\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/s_app_touch_fix_area_bg\"\n                android:gravity=\"left\"\n                android:padding=\"5dp\"\n                android:layout_marginBottom=\"10dp\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanTouchFix_qmui_fix_behavior\"/>\n\n            <com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView\n                android:id=\"@+id/touch_fix_tv_1\"\n                style=\"@style/QDCommonDescription\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/s_app_touch_fix_area_bg\"\n                android:gravity=\"left\"\n                android:padding=\"5dp\"/>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"1px\"\n                android:background=\"?attr/qmui_skin_support_color_separator_darken\"/>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:layout_marginTop=\"20dp\"\n                android:text=\"@string/spanTouchFix_system_behavior\"/>\n\n            <LinearLayout\n                android:id=\"@+id/click_area_1\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/s_app_touch_fix_area_bg\"\n                android:orientation=\"vertical\"\n                android:padding=\"20dp\">\n\n                <TextView\n                    android:id=\"@+id/sysytem_tv_2\"\n                    style=\"@style/QDCommonDescription\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginBottom=\"0dp\"\n                    android:background=\"?attr/qmui_config_color_gray_8\"\n                    android:gravity=\"left\"\n                    android:padding=\"5dp\"/>\n            </LinearLayout>\n\n            <TextView\n                style=\"@style/QDCommonTitle\"\n                android:text=\"@string/spanTouchFix_qmui_fix_behavior\"\n                android:layout_marginTop=\"10dp\"/>\n\n            <LinearLayout\n                android:id=\"@+id/click_area_2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/s_app_touch_fix_area_bg\"\n                android:orientation=\"vertical\"\n                android:padding=\"20dp\">\n\n                <com.qmuiteam.qmui.widget.textview.QMUISpanTouchFixTextView\n                    android:id=\"@+id/touch_fix_tv_2\"\n                    style=\"@style/QDCommonDescription\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginBottom=\"0dp\"\n                    android:background=\"?attr/qmui_config_color_gray_8\"\n                    android:gravity=\"left\"\n                    android:padding=\"5dp\"/>\n            </LinearLayout>\n\n        </LinearLayout>\n    </ScrollView>\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_verticaltextview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.layout.QMUIConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"?attr/app_skin_common_background\"\n    app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <ScrollView\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:paddingLeft=\"@dimen/common_content_spacing\"\n            android:paddingRight=\"@dimen/common_content_spacing\"\n            android:paddingTop=\"40dp\">\n\n            <com.qmuiteam.qmui.widget.QMUIVerticalTextView\n                android:id=\"@+id/verticalTextView\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"250dp\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginBottom=\"40dp\"\n                android:background=\"?attr/qmui_config_color_gray_9\"\n                android:lineSpacingExtra=\"6dp\"\n                android:padding=\"6dp\"\n                android:textColor=\"?attr/qmui_config_color_gray_1\"\n                android:textSize=\"14sp\"/>\n\n            <EditText\n                android:id=\"@+id/verticalTextView_editText\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:background=\"@drawable/qmui_divider_bottom_bitmap\"\n                android:hint=\"在此输入文字, 看垂直排版的效果\"\n                android:paddingBottom=\"6dp\"\n                android:singleLine=\"true\"\n                android:textColor=\"@color/app_color_description\"\n                android:textSize=\"16sp\"/>\n        </LinearLayout>\n\n    </ScrollView>\n\n</com.qmuiteam.qmui.layout.QMUIConstraintLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_viewhelper_animation_show_and_hide.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <RelativeLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\"\n        android:paddingLeft=\"@dimen/qmui_content_spacing_horizontal\"\n        android:paddingRight=\"@dimen/qmui_content_spacing_horizontal\">\n\n        <TextView\n            android:id=\"@+id/popup\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"90dp\"\n            android:layout_above=\"@+id/actiontBtn\"\n            android:layout_marginBottom=\"30dp\"\n            android:background=\"@color/app_color_theme_5\"\n            android:gravity=\"center\"\n            android:paddingLeft=\"5dp\"\n            android:paddingRight=\"5dp\"\n            android:textColor=\"@color/qmui_config_color_white\"\n            android:textSize=\"18sp\"\n            android:visibility=\"gone\"/>\n\n        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n            android:id=\"@+id/actiontBtn\"\n            style=\"@style/QMUI.RoundButton\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"48dp\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/progress_start_button_text\"/>\n\n    </RelativeLayout>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_viewhelper_background_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/topbar\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:background=\"@color/qmui_config_color_white\"\n        app:qmui_skin_background=\"?attr/app_skin_common_background\">\n\n        <LinearLayout\n            android:id=\"@+id/container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:gravity=\"center\"\n            android:paddingLeft=\"@dimen/qmui_content_spacing_horizontal\"\n            android:paddingRight=\"@dimen/qmui_content_spacing_horizontal\">\n\n            <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton\n                android:id=\"@+id/actiontBtn\"\n                style=\"@style/QMUI.RoundButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"48dp\"\n                android:layout_weight=\"1\"\n                android:text=\"@string/progress_start_button_text\"/>\n\n        </LinearLayout>\n    </LinearLayout>\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout2>\n"
  },
  {
    "path": "qmuidemo/src/main/res/layout/fragment_webview_explorer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<com.qmuiteam.qmui.widget.QMUIWindowInsetLayout\n    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/qmui_skin_support_color_background\">\n\n    <com.qmuiteam.qmui.widget.webview.QMUIWebViewContainer\n        android:id=\"@+id/webview_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n    </com.qmuiteam.qmui.widget.webview.QMUIWebViewContainer>\n\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\">\n        <ProgressBar\n            android:id=\"@+id/progress_bar\"\n            style=\"?android:attr/progressBarStyleHorizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:maxHeight=\"2dp\"\n            android:minHeight=\"2dp\"\n            android:layout_gravity=\"bottom\"\n            android:progressDrawable=\"@drawable/web_explorer_progress\"/>\n    </com.qmuiteam.qmui.widget.QMUITopBarLayout>\n\n\n</com.qmuiteam.qmui.widget.QMUIWindowInsetLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/home_item_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"120dp\"\n    android:background=\"?attr/qmui_skin_support_s_list_item_bg_1\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"8dp\"\n    android:paddingRight=\"8dp\"\n    app:qmui_skin_background=\"?attr/qmui_skin_support_s_list_item_bg_1\">\n\n    <ImageView\n        android:id=\"@+id/item_icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:contentDescription=\"Item Icon\"\n        app:qmui_skin_tint_color=\"?attr/app_skin_common_img_tint_color\"/>\n\n    <TextView\n        android:id=\"@+id/item_name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_horizontal|top\"\n        android:lines=\"2\"\n        android:textColor=\"?attr/qmui_config_color_gray_6\"\n        android:textSize=\"11sp\"\n        app:qmui_skin_text_color=\"?attr/app_skin_home_text_color\"/>\n</LinearLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/home_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <com.qmuiteam.qmui.widget.QMUITopBarLayout\n        android:id=\"@+id/topbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"true\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n</merge>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/recycler_linear_layout_simple_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/item\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:textAppearance=\"?android:attr/textAppearanceListItemSmall\"\n    android:gravity=\"center_vertical\"\n    android:paddingLeft=\"?attr/qmui_content_padding_horizontal\"\n    android:paddingRight=\"?attr/qmui_content_padding_horizontal\"\n    android:minHeight=\"?android:attr/listPreferredItemHeightSmall\"\n    android:background=\"@drawable/qmui_divider_bottom_bitmap\"/>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/recycler_view_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/textView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"24dp\"\n        android:layout_marginLeft=\"24dp\"\n        android:layout_marginRight=\"24dp\"\n        android:layout_marginTop=\"24dp\"\n        android:background=\"@drawable/pager_layout_item_bg\"\n        android:gravity=\"center\"\n        android:textColor=\"#fff\"\n        android:textSize=\"40sp\"/>\n</RelativeLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/simple_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/text\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:textAppearance=\"?android:attr/textAppearanceListItemSmall\"\n    android:gravity=\"center_vertical\"\n    android:paddingLeft=\"?attr/qmui_content_padding_horizontal\"\n    android:paddingRight=\"?attr/qmui_content_padding_horizontal\"\n    android:minHeight=\"?android:attr/listPreferredItemHeightSmall\"\n    android:background=\"?attr/qmui_skin_support_s_list_item_bg_1\"\n    app:qmui_skin_background=\"?attr/qmui_skin_support_s_list_item_bg_1\"\n    app:qmui_skin_text_color=\"?attr/app_skin_common_title_text_color\"/>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/simple_list_item_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<androidx.appcompat.widget.AppCompatTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/text\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\"\n    android:paddingLeft=\"?attr/qmui_content_padding_horizontal\"\n    android:paddingRight=\"?attr/qmui_content_padding_horizontal\"\n    android:minHeight=\"?android:attr/listPreferredItemHeightSmall\"\n    android:background=\"?attr/qmui_skin_support_s_list_item_bg_2\"\n    app:qmui_skin_background=\"?attr/qmui_skin_support_s_list_item_bg_2\"\n    app:qmui_skin_text_color=\"?attr/app_skin_common_title_text_color\"/>"
  },
  {
    "path": "qmuidemo/src/main/res/layout/tipdialog_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              xmlns:tools=\"http://schemas.android.com/tools\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:gravity=\"center\"\n              android:orientation=\"horizontal\"\n              tools:ignore=\"UseCompoundDrawables\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:contentDescription=\"自定义 icon\"\n        android:src=\"@drawable/qmui_icon_notify_info\"/>\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"20dp\"\n        android:text=\"自定义结构\"\n        android:textColor=\"@color/qmui_config_color_white\"\n        android:textSize=\"16sp\"/>\n\n</LinearLayout>"
  },
  {
    "path": "qmuidemo/src/main/res/values/attr.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <attr name=\"app_primary_color\" format=\"color\" /> <!-- topbar -->\n    <attr name=\"app_content_bg_color\" format=\"color\" /> <!-- content-->\n</resources>"
  },
  {
    "path": "qmuidemo/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <!-- common -->\n    <color name=\"app_color_blue\">#00A8E1</color>\n    <color name=\"app_color_blue_disabled\">#8000A8E1</color>\n    <color name=\"app_color_blue_pressed\">#c000A8E1</color>\n\n    <color name=\"app_color_blue_2\">#31BDF3</color>\n    <color name=\"app_color_blue_2_disabled\">#7F31BDF3</color>\n    <color name=\"app_color_blue_2_pressed\">#7F31BDF3</color>\n\n    <color name=\"app_color_blue_3\">#EBF9FF</color>\n    <color name=\"app_color_blue_3_disabled\">#7FEBF9FF</color>\n    <color name=\"app_color_blue_3_pressed\">#7FEBF9FF</color>\n\n    <color name=\"app_color_theme_1\">#EF5362</color> <!-- Grapefruit -->\n    <color name=\"app_color_theme_2\">#FE6D4B</color> <!-- Bittersweet -->\n    <color name=\"app_color_theme_3\">#FFCF47</color> <!-- Sunflower -->\n    <color name=\"app_color_theme_4\">#9FD661</color> <!-- Grass -->\n    <color name=\"app_color_theme_5\">#3FD0AD</color> <!-- Mint -->\n    <color name=\"app_color_theme_6\">#2BBDF3</color> <!-- Aqua -->\n    <color name=\"app_color_theme_7\">#5A9AEF</color> <!-- Blue Jeans -->\n    <color name=\"app_color_theme_8\">#AC8FEF</color> <!-- Lavender -->\n    <color name=\"app_color_theme_9\">#EE85C1</color> <!-- Pink Rose -->\n\n    <color name=\"app_color_description\">@color/qmui_config_color_gray_5</color>\n\n    <color name=\"bar_divider\">#D4D6D8</color>\n    <color name=\"tab_panel_bg\">#ffffff</color>\n    <color name=\"tab_panel_divider\">@color/bar_divider</color>\n\n    <!-- Components 组件 -->\n    <color name=\"radiusImageView_border_color\">@color/app_color_theme_2</color>\n    <color name=\"radiusImageView_selected_border_color\">@color/app_color_theme_1</color>\n    <color name=\"radiusImageView_selected_mask_color\">#7FEF5362</color>\n\n    <!-- Helper 系列界面 -->\n\n    <!-- ColorHelper 颜色工具 -->\n    <color name=\"colorHelper_square_alpha_background\">@color/app_color_theme_4</color>\n    <color name=\"colorHelper_square_from_ratio_background\">@color/app_color_blue</color>\n    <color name=\"colorHelper_square_to_ratio_background\">@color/app_color_theme_3</color>\n</resources>"
  },
  {
    "path": "qmuidemo/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <dimen name=\"home_tab_height\">49dp</dimen>\n    <dimen name=\"common_content_spacing\">@dimen/qmui_content_spacing_horizontal</dimen>\n\n    <!-- ListView -->\n    <dimen name=\"list_divider_height\">1px</dimen>\n    <dimen name=\"list_divider_height_negative\">-1px</dimen>\n    <!-- Helper 系列界面 -->\n\n    <!-- ColorHelper 颜色工具 -->\n    <dimen name=\"colorHelper_square_length\">46dp</dimen>\n    <dimen name=\"colorHelper_square_margin\">23dp</dimen>\n    <dimen name=\"colorHelper_square_desc_marginTop\">10dp</dimen>\n    <dimen name=\"colorHelper_column_marginBottom\">35dp</dimen>\n\n    <!-- DeviceHelper 设备检测工具 -->\n    <dimen name=\"deviceHelper_column_marginBottom\">30dp</dimen>\n\n    <!-- DrawableHelper Drawable 工具 -->\n    <dimen name=\"drawableHelper_column_marginBottom\">35dp</dimen>\n    <dimen name=\"drawableHelper_item_marginHorizontal\">20dp</dimen>\n    <dimen name=\"drawableHelper_common_shape_size\">50dp</dimen>\n\n    <!-- Span Utils 各种 Span 工具 -->\n    <dimen name=\"spanUtils_column_marginBottom\">35dp</dimen>\n    <dimen name=\"spanUtils_common_textSize\">16sp</dimen>\n</resources>\n"
  },
  {
    "path": "qmuidemo/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n    <item name=\"qmuidemo\" type=\"id\"/>\n    <item name=\"main_container\" type=\"id\"/>\n\n    <item name=\"empty_button\" type=\"id\"/>\n\n    <item name=\"topbar_right_about_button\" type=\"id\"/>\n    <item name=\"topbar_right_change_button\" type=\"id\"/>\n\n    <item name=\"layout_for_test\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "qmuidemo/src/main/res/values/strings.xml",
    "content": "<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n\n    <!-- common -->\n    <string name=\"app_name\">QMUI</string>\n    <string name=\"common_example\">示例图片</string>\n    <string name=\"app_upgrade_tip_title\">Release Notes (%1$s)</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"cancel\">CANCEL</string>\n\n    <!-- Components 系列界面 -->\n\n    <!-- EmptyViewFragment 空界面展示界面 -->\n    <string name=\"emptyView_mode_title_loading\">显示Loading</string>\n    <string name=\"emptyView_mode_title_single_text\">显示一行提示语</string>\n    <string name=\"emptyView_mode_title_double_text\">显示两行提示语</string>\n    <string name=\"emptyView_mode_title_single_text_and_button\">显示一行文字和按钮</string>\n    <string name=\"emptyView_mode_title_double_text_and_button\">显示两行文字和按钮</string>\n\n    <string name=\"emptyView_mode_desc_single\">这是一行提示语</string>\n    <string name=\"emptyView_mode_desc_double\">我是一个EmptyView</string>\n    <string name=\"emptyView_mode_desc_detail_double\">通过右上角的菜单来切换我的不同状态</string>\n    <string name=\"emptyView_mode_desc_fail_title\">加载失败</string>\n    <string name=\"emptyView_mode_desc_fail_desc\">请检测网络是否能正常连接</string>\n    <string name=\"emptyView_mode_desc_retry\">点击重试</string>\n\n    <!-- TabSegmentFragment 选项卡展示界面 -->\n    <string name=\"tabSegment_item_1_title\">Item 1</string>\n    <string name=\"tabSegment_item_2_title\">Item 2</string>\n\n    <string name=\"tabSegment_item_1_content\">这是第一个 Item 的内容区</string>\n    <string name=\"tabSegment_item_2_content\">这是第二个 Item 的内容区</string>\n\n    <string name=\"tabSegment_mode_general\">简单文字</string>\n    <string name=\"tabSegment_mode_bottom_indicator\">文字 + 底部 indicator</string>\n    <string name=\"tabSegment_mode_top_indicator\">文字 + 顶部 indicator</string>\n    <string name=\"tabSegment_mode_indicator_with_content\">文字 + indicator 长度不要跟随内容长度</string>\n    <string name=\"tabSegment_mode_left_icon_and_auto_tint\">文字 + icon(支持四个方向) + 自动着色选中态 icon</string>\n    <string name=\"tabSegment_mode_sign_count\">显示红点</string>\n    <string name=\"tabSegment_mode_icon_change\">选中态更换 icon</string>\n    <string name=\"tabSegment_mode_muti_color\">不同 item，不同文字(icon)颜色</string>\n    <string name=\"tabSegment_mode_change_content_by_index\">根据 index 更新 tab 文案</string>\n    <string name=\"tabSegment_mode_replace_tab_by_index\">根据 index 完全替换 tab</string>\n    <string name=\"tabSegment_mode_scale_selected\">放大选中态</string>\n    <string name=\"tabSegment_mode_change_gravity\">修改 Gravity</string>\n\n    <!-- ProgressBarFragment 进度条展示界面 -->\n    <string name=\"progress_start_button_text\">点击开始</string>\n    <string name=\"progress_back_button_text\">回滚进度</string>\n\n    <string name=\"circularImageView_item_xml_circle_title\">剪裁为圆形+边框+点击效果</string>\n    <string name=\"circularImageView_item_xml_oval_title\">剪裁为椭圆形+边框+点击效果</string>\n    <string name=\"circularImageView_item_xml_round_rect_title\">剪裁为圆角矩形+边框+点击效果</string>\n    <string name=\"circularImageView_item_xml_scale_type_desc\">下面两张图片，大小固定，第一张图片比 View 小，\n        第二张图片比 View大，可以观察 QMUIRadiusImageView 与 QMUIRadiusImageView2 的不同，便于观察，图片设置了黑色背景，</string>\n\n    <string name=\"circularImageView_modify_tip\">可以通过右上角的按钮动态修改下图的效果</string>\n    <string name=\"circularImageView_modify_1\">改变边框颜色与宽度</string>\n    <string name=\"circularImageView_modify_2\">改变选中态边框颜色与宽度</string>\n    <string name=\"circularImageView_modify_3\">改变选中态 Mask 的颜色（带透明度）</string>\n    <string name=\"circularImageView_modify_4\">手工切换选中态</string>\n    <string name=\"circularImageView_modify_5\">改变圆角大小</string>\n    <string name=\"circularImageView_modify_6\">变为圆形</string>\n    <string name=\"circularImageView_modify_7\">变为椭圆形</string>\n    <string name=\"circularImageView_modify_8\">禁止使用touchSelect态</string>\n    <string name=\"circularImageView_modify_9\">重置为初始态</string>\n\n    <!-- PopupFragment 浮层展示界面 -->\n    <string name=\"popup_normal_action_button_text_show\">显示普通浮层</string>\n    <string name=\"popup_list_action_button_text_show\">显示列表浮层</string>\n    <string name=\"popup_dim_amount_action_button_text_show\">显示带遮罩的普通浮层</string>\n    <string name=\"popup_dynamic_update_content\">显示浮层，并且动态更新内容</string>\n    <string name=\"fullscreen_popup_with_close_btn\">全屏浮层，带关闭按钮</string>\n    <string name=\"fullscreen_popup_with_input\">全屏浮层，带输入</string>\n    <string name=\"quick_action\">快捷菜单</string>\n    <string name=\"quick_action_more\">快捷菜单(很多item)</string>\n\n    <!-- LinkTextView 展示界面 -->\n    <string name=\"linkTextView_auto_identify\">URL/Mail/Tel自动识别</string>\n    <string name=\"linkTextView_auto_identify_word\">可以自动识别网页链接，例如 http://www.qmuiteam.com。\\n可以自动识别电话号码，例如 13600000000。\\n也可以识别邮件地址，例如 qmuiteam@gmail.com。\\n我们可以为其添加自定义的点击事件。</string>\n\n    <!-- SpanTouchFixTextView -->\n    <string name=\"spanTouchFix_description\">QMUISpanTouchFixTextView 主要用于修正 TextView 添加 ClickSpan 后的点击行为与事件传递</string>\n    <string name=\"spanTouchFix_system_behavior\">TextView系统行为</string>\n    <string name=\"spanTouchFix_qmui_fix_behavior\">QMUI修复后行为</string>\n\n    <!-- PullRefreshView -->\n    <string name=\"pull_refresh_default_offset_calculator\">默认效果: 下拉到一定距离后 Loading 不动</string>\n    <string name=\"pull_refresh_follow_offset_calculator\">Loading 一直跟随下拉</string>\n    <string name=\"pull_refresh_center_gravity_offset_calculator\">Loading 保持在下拉区域的中间</string>\n\n    <!-- StickySectionLayout -->\n    <string name=\"sticky_section_decoration_list_footer\">这是整个列表的 footer</string>\n    <string name=\"sticky_section_decoration_section_top_tip\">这是 section 列表头部的提示语</string>\n    <string name=\"sticky_section_decoration_section_bottom_tip\">这是 section 列表底部的提示语</string>\n\n\n    <string name=\"system_behavior_1\">\\@qmui:点击span区域会同时出发span的点击事件和 TextView 的点击事件 #qmui#</string>\n    <string name=\"system_behavior_2\">\\@qmui: 系统默认情况下，TextView 不添加点击事件，然后 TextView 的父元素添加点击事件，\n        那么点击 TextView 将无法触发父元素的点击事件。#qmui#</string>\n\n    <string name=\"span_touch_fix_1\">\\@qmui:span的点击事件与textView的点击事件完全分离#qmui#</string>\n    <string name=\"span_touch_fix_2\">\\@qmui:默认情况下，如果textView添加了clickSpan,#qmui#那么textView的事件将无法冒泡到父级。\n        但在某些场景（例如微博评论需要整条可点），我们需要将事件冒泡给父元素进行处理。#qmui#</string>\n\n    <!-- Helper 系列界面 -->\n\n    <!-- ColorHelper 颜色工具 -->\n    <string name=\"colorHelper_alpha_title\">设置颜色的 alpha 值</string>\n    <string name=\"colorHelper_ratio_title\">根据比例，在两个 color 值之间计算出一个 color 值</string>\n    <string name=\"colorHelper_transform_title\">将 color 颜色值转换为字符串</string>\n\n    <string name=\"colorHelper_squqre_origin\">原始</string>\n    <string name=\"colorHelper_squqre_alpha\">%1$.1f alpha</string>\n\n    <!-- DeviceHelper 设备检测工具 -->\n    <string name=\"deviceHelper_tablet_title\">判断是否为平板设备</string>\n    <string name=\"deviceHelper_flyme_title\">判断是否为 Flyme 系统</string>\n    <string name=\"deviceHelper_miui_title\">判断是否为 MIUI 系统</string>\n    <string name=\"deviceHelper_meizu_title\">判断是否为魅族手机</string>\n\n    <!-- DrawableHelper Drawable 工具 -->\n    <string name=\"drawableHelper_create_from_view_title\">从一个 View 创建 Bitmap</string>\n    <string name=\"drawableHelper_create_from_view_button\">以本界面 Root View 为例</string>\n    <string name=\"drawableHelper_solid_image_title\">创建一张指定大小的纯色图片，支持圆角</string>\n    <string name=\"drawableHelper_circle_gradient_title\">创建一张渐变图片，支持圆角</string>\n    <string name=\"drawableHelper_tint_color_title\">设置 Drawable 的颜色</string>\n    <string name=\"drawableHelper_separator_title\">创建带上分隔线或下分隔线的 Drawable</string>\n\n    <!-- StatusBarHelper 状态栏工具 -->\n    <string name=\"statusBarHelper_translucentActivity_desc\">通过在 Activity 中调用 QMUIStatusBarHelper.translucent() 方法，即可实现沉浸式状态栏的效果</string>\n    <string name=\"statusBarHelper_statusBar_height_result\">状态栏实际高度为 %1$d</string>\n\n    <!-- Span Utils 各种 Span 工具 -->\n    <string name=\"spanUtils_side_icon_title\">在文字左边或者右边直接添加 Icon</string>\n    <string name=\"spanUtils_align_middle_title\">支持垂直居中的 ImageSpan</string>\n    <string name=\"spanUtils_margin_image_title\">支持增加左右间距的 ImageSpan</string>\n    <string name=\"spanUtils_block_space_title\">整行的空白 Span，可用来用于制作段间距</string>\n    <string name=\"spanUtils_custom_type_face_title\">自定义部分文字的字体</string>\n    <string name=\"spanUtils_rmb\">¥</string><!-- ¥相当于&#xA5 -->\n\n    <!-- 其他界面 -->\n\n    <!-- 关于界面 -->\n    <string name=\"about_title\">关于</string>\n    <string name=\"about_item_homepage\">访问官网</string>\n    <string name=\"about_item_github\">GitHub</string>\n    <string name=\"about_copyright\">© %1$s QMUI Team All rights reserved.</string>\n\n</resources>\n"
  },
  {
    "path": "qmuidemo/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <style name=\"QDtextAppearanceListItem\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_black</item>\n        <item name=\"android:textSize\">18sp</item>\n    </style>\n\n    <style name=\"QDTextAppearanceListItemSmall\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_4</item>\n        <item name=\"android:textSize\">16sp</item>\n    </style>\n\n    <style name=\"QDCommonTitle\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginBottom\">6dp</item>\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"android:textSize\">17sp</item>\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"qmui_skin_text_color\">?attr/app_skin_common_title_text_color</item>\n    </style>\n\n    <style name=\"QDCommonDescription\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_marginBottom\">20dp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textColor\">@color/app_color_description</item>\n        <item name=\"android:textSize\">15sp</item>\n    </style>\n\n    <style name=\"QDTopBar\" parent=\"QMUI.TopBar\">\n        <item name=\"qmui_topbar_height\">48dp</item>\n        <item name=\"qmui_topbar_image_btn_height\">48dp</item>\n        <item name=\"qmui_topbar_title_bold\">true</item>\n        <item name=\"qmui_topbar_text_btn_bold\">true</item>\n    </style>\n\n    <style name=\"QDRoundButtonStyle\" parent=\"@style/Button.Compat\">\n        <item name=\"android:layout_height\">40dp</item>\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:paddingLeft\">?attr/qmui_content_spacing_horizontal</item>\n        <item name=\"android:paddingRight\">?attr/qmui_content_spacing_horizontal</item>\n        <item name=\"qmui_borderColor\">?attr/qmui_skin_support_round_btn_border_color</item>\n        <item name=\"qmui_backgroundColor\">@color/s_app_color_blue_3</item>\n        <item name=\"android:textColor\">@color/s_app_color_blue_2</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n\n    <style name=\"button_wrapper_style\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:paddingTop\">24dp</item>\n        <item name=\"android:paddingBottom\">24dp</item>\n        <item name=\"qmui_bottomDividerHeight\">1px</item>\n        <item name=\"qmui_bottomDividerColor\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_separator_bottom\">?attr/qmui_skin_support_color_separator</item>\n    </style>\n\n    <style name=\"DialogTheme2\" parent=\"QMUI.Dialog\">\n        <item name=\"qmui_dialog_inset_hor\">64dp</item>\n        <item name=\"qmui_dialog_title_style\">@style/DialogTheme2TitleStyle</item>\n        <item name=\"qmui_dialog_action_container_style\">@style/DialogTheme2ActionContainerStyle\n        </item>\n        <item name=\"qmui_dialog_action_style\">@style/DialogTheme2ActionStyle</item>\n        <item name=\"qmui_dialog_message_content_style\">@style/DialogTheme2MessageContentStyle</item>\n        <item name=\"qmui_dialog_menu_container_style\">@style/DialogTheme2MenuContainerStyle</item>\n        <item name=\"qmui_dialog_menu_item_style\">@style/DialogTheme2MenuItemStyle</item>\n    </style>\n\n    <style name=\"ReleaseDialogTheme\" parent=\"DialogTheme2\">\n        <item name=\"qmui_dialog_inset_hor\">40dp</item>\n        <item name=\"qmui_dialog_message_content_style\">@style/ReleaseMessageContentStyle</item>\n    </style>\n\n    <style name=\"DialogTheme2TitleStyle\" parent=\"QMUI.Dialog.Title\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"android:textSize\">17sp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingTop\">28dp</item>\n        <item name=\"android:singleLine\">false</item>\n        <item name=\"android:lineSpacingExtra\">3dp</item>\n    </style>\n\n    <style name=\"DialogTheme2ActionContainerStyle\">\n        <item name=\"android:paddingLeft\">0dp</item>\n        <item name=\"android:paddingRight\">0dp</item>\n        <item name=\"android:paddingTop\">0dp</item>\n        <item name=\"android:paddingBottom\">0dp</item>\n        <item name=\"qmui_dialog_action_container_justify_content\">stretch</item>\n        <item name=\"qmui_dialog_action_height\">56dp</item>\n        <item name=\"qmui_dialog_action_space\">8dp</item>\n        <item name=\"qmui_topDividerColor\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_topDividerHeight\">1px</item>\n    </style>\n\n    <style name=\"DialogTheme2ActionStyle\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_blue</item>\n        <item name=\"android:textSize\">15sp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:minWidth\">64dp</item>\n        <item name=\"android:background\">@null</item>\n        <item name=\"qmui_dialog_action_button_padding_horizontal\">12dp</item>\n        <item name=\"qmui_dialog_action_icon_space\">6dp</item>\n        <item name=\"qmui_dialog_positive_action_text_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_dialog_negative_action_text_color\">?attr/qmui_config_color_red</item>\n    </style>\n\n    <style name=\"DialogTheme2MessageContentStyle\" parent=\"QMUI.Dialog.MessageContent\">\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_4</item>\n        <item name=\"android:textSize\">13sp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:paddingTop\">13dp</item>\n        <item name=\"android:paddingBottom\">27dp</item>\n        <item name=\"android:lineSpacingExtra\">5dp</item>\n        <item name=\"qmui_paddingTopWhenNotTitle\">27dp</item>\n    </style>\n\n    <style name=\"ReleaseMessageContentStyle\" parent=\"DialogTheme2MessageContentStyle\">\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:gravity\">left</item>\n    </style>\n\n    <style name=\"DialogTheme2MenuItemStyle\" parent=\"@style/QMUI.Dialog.MenuItem\">\n        <item name=\"android:textSize\">16sp</item>\n        <item name=\"android:textColor\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"android:paddingLeft\">32dp</item>\n        <item name=\"android:paddingRight\">32dp</item>\n    </style>\n\n    <style name=\"DialogTheme2MenuContainerStyle\" parent=\"@style/QMUI.Dialog.MenuContainer\">\n        <item name=\"qmui_dialog_menu_item_height\">52dp</item>\n        <item name=\"qmui_dialog_menu_container_padding_top_when_title_exist\">23dp</item>\n        <item name=\"qmui_dialog_menu_container_padding_bottom_when_action_exist\">24dp</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "qmuidemo/src/main/res/values/theme.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<resources>\n    <!-- skin attr define-->\n    <attr name=\"app_skin_common_background\" format=\"color|reference\" />\n    <attr name=\"app_skin_common_background_1\" format=\"color|reference\" />\n    <attr name=\"app_skin_common_img_tint_color\" format=\"color|reference\" />\n    <attr name=\"app_skin_home_text_color\" format=\"color|reference\" />\n    <attr name=\"app_skin_common_title_text_color\" format=\"color|reference\" />\n    <attr name=\"app_skin_common_desc_text_color\" format=\"color|reference\" />\n    <attr name=\"app_skin_btn_test_bg\" format=\"color|reference\" />\n    <attr name=\"app_skin_btn_test_border\" format=\"color|reference\" />\n    <attr name=\"app_skin_btn_test_bg_single\" format=\"color|reference\" />\n    <attr name=\"app_skin_btn_test_border_single\" format=\"color|reference\" />\n    <attr name=\"app_skin_progress_bar_bg_color\" format=\"color|reference\"/>\n    <attr name=\"app_skin_progress_bar_progress_color\" format=\"color|reference\"/>\n    <attr name=\"app_skin_progress_bar_text_color\" format=\"color|reference\"/>\n    <attr name=\"app_skin_span_normal_text_color\" format=\"color\"/>\n    <attr name=\"app_skin_span_pressed_text_color\" format=\"color\"/>\n    <attr name=\"app_skin_span_normal_bg_color\" format=\"color\"/>\n    <attr name=\"app_skin_span_pressed_bg_color\" format=\"color\"/>\n    <attr name=\"app_skin_alpha_test\" format=\"float\"/>\n\n\n\n\n    <style name=\"AppTheme\" parent=\"QMUI.Compat.NoActionBar\">\n        <!-- 配置Android提供的theme -->\n        <item name=\"android:textAppearanceListItemSmall\">@style/QDTextAppearanceListItemSmall</item>\n        <item name=\"android:textAppearanceListItem\">@style/QDtextAppearanceListItem</item>\n        <item name=\"android:listPreferredItemHeight\">?attr/qmui_list_item_height_higher</item>\n        <item name=\"android:listPreferredItemHeightSmall\">?attr/qmui_list_item_height</item>\n\n        <!-- 配置qmui提供的theme -->\n        <item name=\"qmui_skin_support_topbar_bg\">?attr/app_primary_color</item>\n        <item name=\"qmui_skin_support_topbar_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_topbar_title_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_topbar_subtitle_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_topbar_text_btn_color_state_list\">@color/s_topbar_btn_color</item>\n        <item name=\"qmui_skin_support_topbar_image_tint_color\">@color/qmui_config_color_white</item>\n        <item name=\"QMUITopBarStyle\">@style/QDTopBar</item>\n\n        <item name=\"qmui_skin_support_tab_bg\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tab_separator_color\">?attr/qmui_skin_support_color_separator</item>\n        <item name=\"qmui_skin_support_tab_normal_color\">?attr/qmui_config_color_gray_6</item>\n        <item name=\"qmui_skin_support_tab_selected_color\">?attr/qmui_config_color_blue</item>\n\n        <item name=\"qmui_skin_support_activity_background\">?attr/app_skin_common_background</item>\n        <item name=\"qmui_skin_support_round_btn_border_color\">@color/s_btn_blue</item>\n        <item name=\"qmui_skin_support_round_btn_text_color\">@color/s_btn_blue</item>\n\n\n        <item name=\"qmui_config_color_blue\">@color/app_color_blue</item>\n        <item name=\"qmui_content_spacing_horizontal\">20dp</item>\n        <item name=\"qmui_content_padding_horizontal\">@dimen/qmui_content_spacing_horizontal</item>\n\n\n        <!-- 配置app自己的theme -->\n        <item name=\"app_primary_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"app_content_bg_color\">@color/qmui_config_color_white</item>\n\n        <item name=\"app_skin_common_background\">@color/qmui_config_color_white</item>\n        <item name=\"app_skin_common_background_1\">@color/qmui_config_color_background</item>\n        <item name=\"app_skin_common_img_tint_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"app_skin_home_text_color\">?attr/qmui_config_color_gray_6</item>\n        <item name=\"app_skin_common_title_text_color\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"app_skin_common_desc_text_color\">@color/app_color_description</item>\n        <item name=\"app_skin_btn_test_bg\">@color/s_app_color_blue_3</item>\n        <item name=\"app_skin_btn_test_border\">@color/s_app_color_blue_to_red</item>\n        <item name=\"app_skin_btn_test_bg_single\">@color/app_color_blue_3</item>\n        <item name=\"app_skin_btn_test_border_single\">@color/app_color_blue</item>\n        <item name=\"app_skin_progress_bar_bg_color\">@color/qmui_config_color_gray_8</item>\n        <item name=\"app_skin_progress_bar_progress_color\">@color/qmui_config_color_blue</item>\n        <item name=\"app_skin_progress_bar_text_color\">@color/qmui_config_color_gray_1</item>\n        <item name=\"app_skin_span_normal_text_color\">@color/app_color_blue</item>\n        <item name=\"app_skin_span_pressed_text_color\">@color/app_color_blue_pressed</item>\n        <item name=\"app_skin_span_normal_bg_color\">@color/qmui_config_color_gray_8</item>\n        <item name=\"app_skin_span_pressed_bg_color\">@color/qmui_config_color_gray_6</item>\n        <item name=\"app_skin_alpha_test\">1</item>\n    </style>\n\n    <style name=\"AppTheme.Launcher\">\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowBackground\">@drawable/launcher_bg</item>\n    </style>\n\n    <!-- blue skin -->\n    <style name=\"app_skin_blue\" parent=\"AppTheme\" />\n\n    <!-- dark skin -->\n    <style name=\"app_skin_dark\" parent=\"AppTheme\">\n        <item name=\"qmui_skin_support_color_separator\">@color/qmui_config_color_25_white</item>\n        <item name=\"qmui_skin_support_tab_bg\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_tab_normal_color\">?attr/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_tab_selected_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_tab_sign_count_view_text_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"qmui_skin_support_tab_sign_count_view_bg_color\">#80FF303A</item>\n        <item name=\"qmui_skin_support_empty_view_loading_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"qmui_skin_support_empty_view_title_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_empty_view_sub_title_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"qmui_skin_support_empty_view_btn_border_color\">@color/s_app_color_gray</item>\n        <item name=\"qmui_skin_support_empty_view_btn_text_color\">@color/s_app_color_gray</item>\n        <item name=\"qmui_skin_support_common_list_title_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_common_list_detail_color\">@color/qmui_config_color_50_white</item>\n        <item name=\"qmui_skin_support_common_list_icon_tint_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_common_list_red_point_tint_color\">?attr/qmui_skin_support_tab_sign_count_view_bg_color\n        </item>\n        <item name=\"qmui_skin_support_common_list_new_drawable\">@drawable/qmui_icon_tip_new</item>\n        <item name=\"qmui_skin_support_common_list_chevron_color\">@color/qmui_config_color_50_white</item>\n        <item name=\"qmui_skin_support_s_list_item_bg_1\">@drawable/s_list_item_bg_dark_1</item>\n        <item name=\"qmui_skin_support_s_list_item_bg_2\">@drawable/s_list_item_bg_dark_2</item>\n\n        <item name=\"qmui_skin_support_dialog_bg\">@color/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_dialog_title_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_dialog_message_text_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"qmui_skin_support_dialog_action_bg\">@null</item>\n        <item name=\"qmui_skin_support_dialog_action_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_dialog_positive_action_text_color\">?attr/qmui_config_color_blue</item>\n        <item name=\"qmui_skin_support_dialog_negative_action_text_color\">?attr/qmui_config_color_red</item>\n        <item name=\"qmui_skin_support_dialog_edit_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_dialog_edit_text_hint_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"qmui_skin_support_dialog_menu_item_text_color\">@color/qmui_config_color_white</item>\n\n        <item name=\"qmui_skin_support_bottom_sheet_bg\">@color/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_bottom_sheet_title_text_color\">@color/qmui_config_color_50_white</item>\n        <item name=\"qmui_skin_support_bottom_sheet_cancel_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_bottom_sheet_list_item_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"qmui_skin_support_bottom_sheet_grid_item_text_color\">@color/qmui_config_color_75_white</item>\n\n        <item name=\"qmui_skin_support_popup_bg\">@color/qmui_config_color_black</item>\n        <item name=\"qmui_skin_support_popup_close_icon\">@drawable/icon_popup_close_with_bg_dark</item>\n        <item name=\"qmui_skin_support_quick_action_item_tint_color\">@color/qmui_config_color_white</item>\n\n        <item name=\"qmui_skin_support_slider_bar_bg_color\">@color/qmui_config_color_25_white</item>\n        <item name=\"qmui_skin_support_seek_bar_color\">@color/qmui_config_color_gray_9</item>\n        <item name=\"qmui_skin_support_slider_bar_progress_color\">@color/app_color_blue_2</item>\n\n        <item name=\"app_primary_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"app_skin_common_background\">@color/qmui_config_color_gray_2</item>\n        <item name=\"app_skin_common_background_1\">@color/qmui_config_color_gray_1</item>\n        <item name=\"app_skin_common_img_tint_color\">@color/qmui_config_color_white</item>\n        <item name=\"app_skin_home_text_color\">?attr/qmui_config_color_gray_8</item>\n        <item name=\"app_skin_common_title_text_color\">@color/qmui_config_color_75_white</item>\n        <item name=\"app_skin_progress_bar_bg_color\">@color/qmui_config_color_gray_1</item>\n        <item name=\"app_skin_progress_bar_progress_color\">@color/qmui_config_color_50_blue</item>\n        <item name=\"app_skin_progress_bar_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"app_skin_span_normal_text_color\">@color/qmui_config_color_white</item>\n        <item name=\"app_skin_span_pressed_text_color\">@color/qmui_config_color_50_white</item>\n        <item name=\"app_skin_span_normal_bg_color\">@color/qmui_config_color_gray_4</item>\n        <item name=\"app_skin_span_pressed_bg_color\">@color/qmui_config_color_gray_2</item>\n        <item name=\"app_skin_alpha_test\">0.5</item>\n    </style>\n\n    <!-- white skin -->\n    <style name=\"app_skin_white\" parent=\"AppTheme\">\n        <item name=\"qmui_skin_support_topbar_title_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_topbar_subtitle_color\">@color/qmui_config_color_gray_3</item>\n        <item name=\"qmui_skin_support_topbar_text_btn_color_state_list\">@color/qmui_config_color_pure_black\n        </item>\n        <item name=\"qmui_skin_support_topbar_image_tint_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_tab_normal_color\">?attr/qmui_config_color_gray_5</item>\n        <item name=\"qmui_skin_support_tab_selected_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_round_btn_border_color\">@color/s_btn_gray</item>\n        <item name=\"qmui_skin_support_round_btn_text_color\">@color/s_btn_gray</item>\n        <item name=\"qmui_skin_support_empty_view_btn_border_color\">@color/s_app_color_gray_dark</item>\n        <item name=\"qmui_skin_support_empty_view_btn_text_color\">@color/s_app_color_gray_dark</item>\n        <item name=\"qmui_skin_support_common_list_title_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_common_list_detail_color\">@color/qmui_config_color_50_pure_black</item>\n        <item name=\"qmui_skin_support_common_list_icon_tint_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_slider_bar_bg_color\">@color/qmui_config_color_25_pure_black</item>\n        <item name=\"qmui_skin_support_slider_bar_progress_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"qmui_skin_support_pull_refresh_view_color\">@color/qmui_config_color_pure_black</item>\n\n        <item name=\"app_primary_color\">@color/qmui_config_color_white</item>\n        <item name=\"app_skin_common_img_tint_color\">@color/qmui_config_color_pure_black</item>\n        <item name=\"app_skin_common_desc_text_color\">@color/qmui_config_color_75_pure_black</item>\n        <item name=\"app_skin_btn_test_bg\">@color/s_app_color_gray</item>\n        <item name=\"app_skin_btn_test_border\">@color/s_app_color_gray_dark</item>\n        <item name=\"app_skin_btn_test_bg_single\">?attr/qmui_config_color_gray_5</item>\n        <item name=\"app_skin_btn_test_border_single\">?attr/qmui_config_color_gray_1</item>\n        <item name=\"app_skin_progress_bar_bg_color\">@color/qmui_config_color_gray_8</item>\n        <item name=\"app_skin_progress_bar_progress_color\">@color/qmui_config_color_gray_1</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "qmuidemo/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Tencent is pleased to support the open source community by making QMUI_Android available.\n\n Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n\n Licensed under the MIT License (the \"License\"); you may not use this file except in\n compliance with the License. You may obtain a copy of the License at\n\n http://opensource.org/licenses/MIT\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is\n distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n either express or implied. See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\" />\n</network-security-config>"
  },
  {
    "path": "settings.gradle.kts",
    "content": "pluginManagement {\n    repositories {\n        mavenCentral()\n    }\n}\n\nincludeBuild(\"./plugin\")\n\ninclude(\":qmuidemo\")\ninclude(\":qmui\")\ninclude(\":lib\")\ninclude(\":compiler\")\ninclude(\":arch\")\ninclude(\":arch-compiler\")\ninclude(\":arch-annotation\")\ninclude(\":type\")\ninclude(\":compose-core\")\ninclude(\":compose\")\ninclude(\":photo\")\ninclude(\":photo-coil\")\ninclude(\":photo-glide\")\ninclude(\":editor\")\n\n\n"
  },
  {
    "path": "type/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "type/build.gradle.kts",
    "content": "import com.qmuiteam.plugin.Dep\n\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    `maven-publish`\n    signing\n    id(\"qmui-publish\")\n}\n\nversion = Dep.QMUI.typeVer\n\n\nandroid {\n    compileSdk = Dep.compileSdk\n\n    defaultConfig {\n        minSdk = Dep.minSdk\n        targetSdk = Dep.targetSdk\n    }\n\n    buildTypes {\n        getByName(\"release\"){\n            isMinifyEnabled = false\n            proguardFiles(getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = Dep.javaVersion\n        targetCompatibility = Dep.javaVersion\n    }\n    kotlinOptions {\n        jvmTarget = Dep.kotlinJvmTarget\n    }\n}\n\ndependencies {\n    implementation(Dep.AndroidX.appcompat)\n    implementation(Dep.AndroidX.annotation)\n    implementation(Dep.AndroidX.coreKtx)\n}"
  },
  {
    "path": "type/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.qmuiteam.qmui.type\"/>\n"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/EnvironmentUpdater.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type\n\nfun interface EnvironmentUpdater {\n    fun update(env: TypeEnvironment)\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/Line.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type\n\nimport android.graphics.Canvas\nimport androidx.core.util.Pools\nimport com.qmuiteam.qmui.type.element.BreakWordLineElement\nimport com.qmuiteam.qmui.type.element.Element\nimport com.qmuiteam.qmui.type.element.NextParagraphElement\nimport com.qmuiteam.qmui.type.element.TextElement\nimport java.util.*\n\nclass Line private constructor() {\n    companion object {\n        private val sLinePool: Pools.Pool<Line> = Pools.SimplePool(16)\n\n        fun acquire(): Line {\n            var line = sLinePool.acquire()\n            if (line == null) {\n                line = Line()\n            }\n            return line\n        }\n    }\n\n    var x = 0\n    var y = 0\n    var widthLimit = 0\n        private set\n    var contentWidth = 0\n        private set\n    var contentHeight = 0\n        private set\n    var layoutWidth = 0\n        private set\n    private val mElements = LinkedList<Element>()\n    private var mVisibleChanged: HashMap<Element, Int>? = null\n\n\n    val size: Int\n        get() = mElements.size\n\n    fun get(i: Int): Element? {\n        return mElements.getOrNull(i)\n    }\n\n    fun init(x: Int, y: Int, widthLimit: Int) {\n        this.x = x\n        this.y = y\n        this.widthLimit = widthLimit\n    }\n\n    fun add(element: Element) {\n        mElements.add(element)\n        contentWidth += element.measureWidth\n        contentHeight = contentHeight.coerceAtLeast(element.measureHeight)\n    }\n\n    fun addFirst(element: Element) {\n        mElements.add(0, element)\n        contentWidth += element.measureWidth\n        contentHeight = contentHeight.coerceAtLeast(element.measureHeight)\n    }\n\n    fun first(): Element? {\n        return if (mElements.isEmpty()) null else mElements[0]\n    }\n\n    fun move(environment: TypeEnvironment?) {\n        for (el in mElements) {\n            el.move(environment!!)\n        }\n    }\n\n    fun handleWordBreak(environment: TypeEnvironment, shouldHandleWordBreak: Boolean): List<Element>? {\n        if (mElements.size == 0 || !shouldHandleWordBreak) {\n            return null\n        }\n        var lastIndex = mElements.size - 1\n        val last = mElements[lastIndex]\n        val next = last.next\n        val back: MutableList<Element> = LinkedList()\n        if (last.wordPart == Element.WORD_PART_WHOLE) {\n            if (last.lineBreakType == Element.LINE_BREAK_TYPE_NOT_END ||\n                next != null && next.lineBreakType == Element.LINE_BREAK_TYPE_NOT_START\n            ) {\n                mElements.removeAt(lastIndex)\n                back.add(last)\n            }\n        } else if (last.wordPart == Element.WORD_PART_END && next != null && next.lineBreakType != Element.LINE_BREAK_TYPE_NOT_START) {\n            // do nothing\n        } else if (last.wordPart == Element.WORD_PART_START) {\n            mElements.removeAt(lastIndex)\n            back.add(last)\n        } else {\n            back.add(last)\n            mElements.removeAt(lastIndex)\n            lastIndex--\n            val min = 0.coerceAtLeast(lastIndex - environment.workBreakMaxTryLength)\n            var find = false\n            while (lastIndex > min) {\n                val el = mElements[lastIndex]\n                if (el.wordPart == Element.WORD_PART_WHOLE || el.wordPart == Element.WORD_PART_END) {\n                    find = true\n                    break\n                } else if (el.lineBreakType == Element.LINE_BREAK_WORD_BREAK_ALLOWED) {\n                    // TODO what if environment had changed after break? the measure may be wrong\n                    val b = BreakWordLineElement()\n                    b.measure(environment)\n                    val backWidth = back.sumOf { it.measureWidth }\n                    if (backWidth >= b.measureWidth) {\n                        find = true\n                        add(b)\n                        break\n                    } else {\n                        back.add(0, el)\n                        mElements.removeAt(lastIndex)\n                        lastIndex--\n                    }\n                } else {\n                    back.add(0, el)\n                    mElements.removeAt(lastIndex)\n                    lastIndex--\n                }\n            }\n            if (!find) {\n                // give up\n                mElements.addAll(back)\n                return null\n            }\n        }\n        if (back.isEmpty()) {\n            return null\n        }\n        for (el in back) {\n            contentWidth -= el.measureWidth\n        }\n        return back\n    }\n\n    private fun hideLastIfSpaceIfNeeded(dropLastIfSpace: Boolean): Boolean {\n        val last = mElements[mElements.size - 1]\n        if (dropLastIfSpace && last is TextElement && last.length == 1 && last.text[0] == ' ' && last.visible != Element.GONE) {\n            changeVisibleInner(last, Element.GONE)\n            contentWidth -= last.measureWidth\n            return true\n        }\n        return false\n    }\n\n    private fun changeVisibleInner(element: Element, visible: Int) {\n        val oldVal = element.visible\n        if (visible == oldVal) {\n            return\n        }\n        if (mVisibleChanged == null) {\n            mVisibleChanged = HashMap()\n        }\n        mVisibleChanged!![element] = oldVal\n        element.visible = visible\n    }\n\n    private fun calculateGapCount(): Int {\n        var ret = 0\n        for (i in 1 until mElements.size) {\n            val el = mElements[i]\n            if (el.visible != Element.GONE &&\n                (el.wordPart == Element.WORD_PART_WHOLE ||\n                        el.wordPart == Element.WORD_PART_START)\n            ) {\n                ret++\n            }\n        }\n        return ret\n    }\n\n    val isMiddleParagraphEndLine: Boolean\n        get() = !mElements.isEmpty() && mElements[mElements.size - 1] is NextParagraphElement\n\n    fun layout(env: TypeEnvironment, dropLastIfSpace: Boolean, isEnd: Boolean) {\n        if (mElements.isEmpty()) {\n            return\n        }\n        hideLastIfSpaceIfNeeded(dropLastIfSpace)\n        layoutWidth = contentWidth\n        val alignment = env.alignment\n        var start = x\n        var addSpace = 0\n        if (alignment === TypeEnvironment.Alignment.RIGHT) {\n            start = x + widthLimit - contentWidth\n        } else if (alignment === TypeEnvironment.Alignment.CENTER) {\n            start = x + (widthLimit - contentWidth) / 2\n        } else if (alignment === TypeEnvironment.Alignment.JUSTIFY) {\n            val remain = widthLimit - contentWidth\n            if (!(isEnd || isMiddleParagraphEndLine) || remain < env.lastLineJustifyMaxWidth) {\n                val gapCount = calculateGapCount()\n                if (gapCount > 0) {\n                    addSpace = remain / gapCount\n                    layoutWidth = widthLimit\n                }\n            }\n        }\n        var x = start\n        for (i in mElements.indices) {\n            val el = mElements[i]\n            if (i > 0 && (el.wordPart == Element.WORD_PART_WHOLE\n                        || el.wordPart == Element.WORD_PART_START)\n            ) {\n                x += addSpace\n                mElements[i - 1].nextGapWidth = addSpace\n            }\n            el.x = x\n            x += el.measureWidth\n            el.y = y + (contentHeight - el.measureHeight) / 2\n        }\n    }\n\n    fun draw(env: TypeEnvironment, canvas: Canvas) {\n        for (element in mElements) {\n            element.draw(env, canvas)\n        }\n    }\n\n    fun restoreVisibleChange() {\n        if (mVisibleChanged != null) {\n            for (entry in mVisibleChanged!!.keys) {\n                val visible = mVisibleChanged!![entry]\n                if (visible != null) {\n                    entry.visible = visible\n                }\n            }\n            mVisibleChanged!!.clear()\n        }\n    }\n\n    fun popAll(): List<Element> {\n        val elements: List<Element> = ArrayList(mElements)\n        mElements.clear()\n        restoreVisibleChange()\n        contentWidth = 0\n        contentHeight = 0\n        layoutWidth = 0\n        return elements\n    }\n\n    fun clear() {\n        mElements.clear()\n        restoreVisibleChange()\n        contentWidth = 0\n        contentHeight = 0\n        layoutWidth = 0\n    }\n\n    fun release() {\n        x = 0\n        y = 0\n        widthLimit = 0\n        contentWidth = 0\n        contentHeight = 0\n        layoutWidth = 0\n        mElements.clear()\n        restoreVisibleChange()\n        sLinePool.release(this)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/LineIndentHandler.kt",
    "content": "package com.qmuiteam.qmui.type\n\nimport com.qmuiteam.qmui.type.element.Element\n\ninterface LineIndentHandler {\n    fun reset()\n    fun processIndent(typeModel: TypeModel, firstElement: Element, newParagraph: Boolean): Int\n}\n\nclass SerialLineIndentHandler(\n    serials: List<Pair<Int, Int>>,\n    private val followIndentForNewParagraphIfNeeded: Boolean = false): LineIndentHandler {\n\n    private val sorted = serials.sortedBy {\n        it.first\n    }\n\n    var currentIntend = 0\n    private var pendingCalculatePair: Pair<Int, Int>? = null\n    var nextIndex = 0\n\n    override fun reset() {\n        currentIntend = 0\n        nextIndex = 0\n    }\n\n    override fun processIndent(typeModel: TypeModel, firstElement: Element, newParagraph: Boolean): Int {\n        if(newParagraph){\n            val pair = sorted.find { it.first == firstElement.index }\n            if(pair != null){\n                currentIntend = 0\n            }else if(!followIndentForNewParagraphIfNeeded){\n                currentIntend = 0\n            } else {\n                // make sure it's calculated.\n                pendingCalculatePair?.let {\n                    currentIntend = calculateIndent(typeModel, it)\n                }\n            }\n            pendingCalculatePair = pair\n        }else{\n            pendingCalculatePair?.let {\n                currentIntend = calculateIndent(typeModel, it)\n            }\n            pendingCalculatePair = null\n        }\n        return currentIntend\n    }\n\n    private fun calculateIndent(typeModel: TypeModel, pair: Pair<Int, Int>): Int {\n        var indent = 0\n        var el = typeModel.getByPos(pair.first)\n        while (el != null && el.start <= pair.second){\n            indent += el.measureWidth\n            el = el.next\n        }\n        return indent\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/LineLayout.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type\n\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Typeface\nimport android.text.TextUtils.TruncateAt\nimport com.qmuiteam.qmui.type.element.*\nimport java.util.*\n\nclass LineLayout {\n    var maxLines = Int.MAX_VALUE\n    var ellipsize: TruncateAt? = null\n    var calculateWholeLines = false\n    var dropLastIfSpace = true\n    var moreText: String? = null\n    var moreTextColor = 0\n    var moreTextTypeface: Typeface? = null\n    var moreUnderlineColor = Color.TRANSPARENT\n    var moreBgColor = 0\n    var moreUnderlineHeight = 0\n    var moreTextFixAtEnd: Boolean = true\n    var typeModel: TypeModel? = null\n    var shouldHandleWordBreak: Boolean = true\n\n    var lineIndentHandler:LineIndentHandler? = null\n\n    private var exactlyHeightMaxLine = Int.MAX_VALUE\n\n    private val mLines: MutableList<Line> = ArrayList()\n\n    var totalLineCount = 0\n        private set\n\n    val lineCount: Int\n        get() {\n            return mLines.size\n        }\n\n    fun getLine(i: Int): Line? {\n        return mLines.getOrNull(i)\n    }\n\n    fun measureAndLayout(env: TypeEnvironment, exactlyHeight: Boolean) {\n        exactlyHeightMaxLine = Int.MAX_VALUE\n        env.clear()\n        release()\n        lineIndentHandler?.reset()\n        if (typeModel == null) {\n            return\n        }\n        var element: Element? = typeModel!!.firstElement()\n        var line = Line.acquire()\n        var y = 0\n        val indent = element?.let { lineIndentHandler?.processIndent(typeModel!!, it, true) } ?: 0\n        line.init(indent, y, env.widthLimit - indent)\n\n        fun addLineAndHandleMaxLineAndNextY(line: Line, isParagraphEndLine: Boolean){\n            mLines.add(line)\n            if(env.lineHeight != -1){\n                y += env.lineHeight.coerceAtLeast(line.contentHeight)\n                checkExactlyHeightMaxLine(env, y, exactlyHeight)\n                if(isParagraphEndLine){\n                    y += env.paragraphSpace\n                }\n            }else{\n                y += line.contentHeight\n                checkExactlyHeightMaxLine(env, y, exactlyHeight)\n                y += if(isParagraphEndLine){\n                    env.paragraphSpace\n                }else{\n                    env.lineSpace\n                }\n            }\n        }\n\n        while (element != null) {\n            element.measure(env)\n            if (element is NextParagraphElement) {\n                line.add(element)\n                line.layout(env, dropLastIfSpace, false)\n                addLineAndHandleMaxLineAndNextY(line, true)\n                if (canInterrupt(element)) {\n                    handleEllipse(env,true)\n                    totalLineCount = mLines.size\n                    return\n                }\n                line = createNewLine(env, element.next, y, true)\n            } else if (line.contentWidth + element.measureWidth > line.widthLimit) {\n                if (mLines.size == 0 && line.size == 0) {\n                    // the width is too small.\n                    line.release()\n                    totalLineCount = mLines.size\n                    return\n                }\n                val back = line.handleWordBreak(env, shouldHandleWordBreak)\n                line.layout(env, dropLastIfSpace, false)\n                addLineAndHandleMaxLineAndNextY(line, false)\n\n                if (canInterrupt(element)) {\n                    totalLineCount = mLines.size\n                    handleEllipse(env,true)\n                    return\n                }\n\n                line = createNewLine(env, back?.firstOrNull() ?: element, y, false)\n                if (back != null && back.isNotEmpty()) {\n                    for (el in back) {\n                        line.add(el)\n                    }\n                }\n                line.add(element)\n            } else {\n                line.add(element)\n            }\n            element = element.next\n        }\n        if (line.size > 0) {\n            line.layout(env, dropLastIfSpace, true)\n            addLineAndHandleMaxLineAndNextY(line, false)\n        } else {\n            line.release()\n        }\n        totalLineCount = mLines.size\n        handleEllipse(env,false)\n    }\n\n    private fun checkExactlyHeightMaxLine(env: TypeEnvironment, y: Int, exactlyHeight: Boolean){\n        if(exactlyHeight && exactlyHeightMaxLine == Int.MAX_VALUE){\n            if(y > env.heightLimit){\n                exactlyHeightMaxLine = mLines.size - 1\n            }else if(y == env.heightLimit){\n                exactlyHeightMaxLine = mLines.size\n            }\n        }\n    }\n\n    private fun createNewLine(env: TypeEnvironment, firstElement: Element?, y: Int, newParagraph: Boolean): Line {\n        val line = Line.acquire()\n        val indent = firstElement?.let { lineIndentHandler?.processIndent(typeModel!!, it, newParagraph) } ?: 0\n        line.init(indent, y, env.widthLimit - indent)\n        return line\n    }\n\n    private fun handleEllipse(env: TypeEnvironment, fromInterrupt: Boolean) {\n        if (mLines.isEmpty() || mLines.size < getUsedMaxLine() || (mLines.size == getUsedMaxLine() && !fromInterrupt)) {\n            return\n        }\n        if (ellipsize == TruncateAt.END) {\n            handleEllipseEnd(env)\n        } else if (ellipsize == TruncateAt.START) {\n            handleEllipseStart(env)\n        } else if (ellipsize == TruncateAt.MIDDLE) {\n            handleEllipseMiddle(env)\n        }\n    }\n\n    private fun handleEllipseEnd(env: TypeEnvironment) {\n        val maxSize = getUsedMaxLine()\n        while (mLines.size > maxSize){\n            val line = mLines[mLines.size - 1]\n            mLines.remove(line)\n            line.release()\n        }\n\n        if(mLines.isEmpty()){\n            return\n        }\n\n        val lastLine = mLines[mLines.size - 1]\n        var limitWidth = lastLine.widthLimit\n        val ellipseElement: Element = TextElement(\"...\", -1, -1)\n        ellipseElement.addSingleEnvironmentUpdater(null) { it.clear() }\n        ellipseElement.measure(env)\n        limitWidth -= ellipseElement.measureWidth\n        var moreElement: Element? = null\n        if (moreText != null && moreText!!.isNotEmpty()) {\n            moreElement = MoreTextElement(moreText!!, -1, -1)\n            val changeTypes: MutableList<Int> = ArrayList()\n            changeTypes.add(TypeEnvironment.TYPE_TEXT_COLOR)\n            changeTypes.add(TypeEnvironment.TYPE_BG_COLOR)\n            changeTypes.add(TypeEnvironment.TYPE_TYPEFACE)\n            changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR)\n            changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH)\n            moreElement.addSingleEnvironmentUpdater(changeTypes, object : EnvironmentUpdater {\n                override fun update(env: TypeEnvironment) {\n                    if (moreTextColor != 0) {\n                        env.textColor = moreTextColor\n                    }\n                    if (moreBgColor != 0) {\n                        env.backgroundColor = moreBgColor\n                    }\n                    if (moreTextTypeface != null) {\n                        env.typeface = moreTextTypeface\n                    }\n                    if (moreUnderlineHeight > 0) {\n                        env.setBorderBottom(moreUnderlineHeight, moreUnderlineColor)\n                    }\n                }\n            })\n            moreElement.measure(env)\n            limitWidth -= moreElement.measureWidth\n        }\n        val contentWidth = lastLine.contentWidth\n        if (contentWidth < limitWidth) {\n            lastLine.restoreVisibleChange()\n        } else {\n            val elements = lastLine.popAll()\n            for (el in elements) {\n                if (el.measureWidth <= limitWidth) {\n                    lastLine.add(el)\n                    limitWidth -= el.measureWidth\n                } else {\n                    break\n                }\n            }\n        }\n        lastLine.add(ellipseElement)\n        if (moreElement != null) {\n            lastLine.add(moreElement)\n        }\n        lastLine.layout(env, dropLastIfSpace, true)\n        if (moreElement != null) {\n            if(moreTextFixAtEnd){\n                moreElement.x = lastLine.x + lastLine.widthLimit - moreElement.measureWidth\n            }else{\n                moreElement.x = lastLine.contentWidth - moreElement.measureWidth\n            }\n\n        }\n    }\n\n    private fun handleEllipseStart(env: TypeEnvironment) {\n        env.clear()\n        val tmpList = mutableListOf<Line>()\n        var tmpY = -1\n        val startIndex = mLines.size - getUsedMaxLine()\n        val minus = mLines[startIndex].y\n        for(i in startIndex until mLines.size){\n            mLines[i].y -= minus\n            tmpY += mLines[i].contentHeight\n            tmpList.add(mLines[i])\n        }\n        mLines.clear()\n        mLines.addAll(tmpList)\n        val ellipseElement: Element = TextElement(\"...\", -1, -1)\n        ellipseElement.addSingleEnvironmentUpdater(null, object : EnvironmentUpdater {\n            override fun update(env: TypeEnvironment) {\n                env.clear()\n            }\n        })\n        ellipseElement.measure(env)\n        val elements: Queue<Element> = LinkedList()\n        elements.add(ellipseElement)\n        for (i in mLines.indices) {\n            val line = mLines[i]\n            val limitWidth = line.widthLimit\n            elements.addAll(line.popAll())\n            while (!elements.isEmpty()) {\n                val el = elements.peek()\n                if (el != null) {\n                    if (el is NextParagraphElement) {\n                        elements.poll()\n                        line.add(el)\n                        el.move(env)\n                        break\n                    }\n                    if (el is BreakWordLineElement) {\n                        elements.poll()\n                        continue\n                    }\n                    if (line.contentWidth + el.measureWidth <= limitWidth) {\n                        elements.poll()\n                        line.add(el)\n                        el.move(env)\n                    } else {\n                        break\n                    }\n                } else {\n                    elements.poll()\n                }\n            }\n            line.handleWordBreak(env, shouldHandleWordBreak)\n            line.layout(env, dropLastIfSpace, false)\n            if (elements.isEmpty()) {\n                return\n            }\n        }\n    }\n\n    private fun handleEllipseMiddle(env: TypeEnvironment) {\n        env.clear()\n        val lines: List<Line> = ArrayList(mLines)\n        mLines.clear()\n        val ellipseElement: Element = TextElement(\"...\", -1, -1)\n        ellipseElement.measure(env)\n        val maxLines = getUsedMaxLine()\n        val ellipseLine = if (maxLines % 2 == 0) maxLines / 2 else (maxLines + 1) / 2\n        for (i in 0 until ellipseLine) {\n            mLines.add(lines[i])\n        }\n        val handleLine = lines[ellipseLine - 1]\n        val limitWidth = handleLine.widthLimit\n        val unHandled: Deque<Element> = LinkedList(handleLine.popAll())\n        while (!unHandled.isEmpty()) {\n            val el = unHandled.peek()\n            if (el != null) {\n                if (handleLine.contentWidth + el.measureWidth <= limitWidth / 2f - ellipseElement.measureWidth / 2) {\n                    unHandled.poll()\n                    handleLine.add(el)\n                    el.move(env)\n                } else {\n                    break\n                }\n            } else {\n                unHandled.poll()\n            }\n        }\n        ellipseElement.measure(env)\n        handleLine.add(ellipseElement)\n        val nextFullShowLine = lines.size - maxLines + ellipseLine\n        var startLine = lines.size - 1\n        // find the latest paragraph end line.\n        for (i in lines.size - 2 downTo nextFullShowLine + 1) {\n            if (lines[i].isMiddleParagraphEndLine) {\n                startLine = i\n            }\n        }\n        for (i in ellipseLine..startLine) {\n            unHandled.addAll(lines[i].popAll())\n        }\n        for (i in startLine downTo nextFullShowLine) {\n            val line = lines[i]\n            while (!unHandled.isEmpty()) {\n                val element = unHandled.peekLast()\n                if (element != null) {\n                    if (element is NextParagraphElement) {\n                        unHandled.pollLast()\n                        continue\n                    }\n                    if (element is BreakWordLineElement) {\n                        unHandled.pollLast()\n                        continue\n                    }\n                    if (line.contentWidth + element.measureWidth <= line.widthLimit) {\n                        unHandled.pollLast()\n                        line.addFirst(element)\n                    } else {\n                        break\n                    }\n                } else {\n                    unHandled.pollLast()\n                }\n            }\n        }\n        val toAdd = LinkedList<Element>()\n        var toAddWidth = 0\n        while (!unHandled.isEmpty()) {\n            val element = unHandled.peekLast()\n            if (element != null) {\n                if (element is NextParagraphElement) {\n                    unHandled.pollLast()\n                    continue\n                }\n                if (element is BreakWordLineElement) {\n                    unHandled.pollLast()\n                    continue\n                }\n                if (handleLine.contentWidth + toAddWidth + element.measureWidth <= handleLine.widthLimit) {\n                    unHandled.pollLast()\n                    toAdd.add(0, element)\n                    toAddWidth = (toAddWidth + element.measureWidth).toInt()\n                } else {\n                    break\n                }\n            } else {\n                unHandled.pollLast()\n            }\n        }\n        val firstUnHandle = unHandled.peekFirst()\n        val lastUnHandle = unHandled.peekLast()\n        var effect = typeModel!!.firstEffect\n        if (firstUnHandle != null && lastUnHandle != null) {\n            val ellipseEffect: MutableList<Element> = ArrayList()\n            while (effect != null && effect.index <= lastUnHandle.index) {\n                if (effect.index >= firstUnHandle.index) {\n                    ellipseEffect.add(effect)\n                }\n                effect = effect.next\n            }\n            if (ellipseEffect.size > 0) {\n                val ignoreEffectElement = IgnoreEffectElement(ellipseEffect)\n                ignoreEffectElement.move(env)\n                handleLine.add(ignoreEffectElement)\n            }\n        }\n        for (el in toAdd) {\n            el.move(env)\n            handleLine.add(el)\n        }\n        handleLine.handleWordBreak(env, shouldHandleWordBreak)\n        handleLine.layout(env, dropLastIfSpace, ellipseLine == lines.size)\n        var lastEnd = handleLine.y + handleLine.contentHeight\n        for (i in nextFullShowLine until lines.size) {\n            val line = lines[i]\n            val prev = lines[i - 1]\n            if (prev.isMiddleParagraphEndLine) {\n                line.y = lastEnd + env.paragraphSpace.coerceAtLeast(env.lineHeight - handleLine.contentHeight)\n            } else {\n                line.y = lastEnd + env.lineSpace.coerceAtLeast(env.lineHeight - handleLine.contentHeight)\n            }\n            lastEnd = line.y + line.contentHeight\n            line.move(env)\n            line.handleWordBreak(env, shouldHandleWordBreak)\n            line.layout(env, dropLastIfSpace, i == lines.size - 1)\n            mLines.add(line)\n        }\n    }\n\n    val maxLayoutWidth: Int\n        get() {\n            var maxWidth = 0\n            for (line in mLines) {\n                maxWidth = line.layoutWidth.coerceAtLeast(maxWidth)\n            }\n            return maxWidth\n        }\n    val contentHeight: Int\n        get() {\n            if (mLines.isEmpty()) {\n                return 0\n            }\n            val last = mLines[mLines.size - 1]\n            return last.y + last.contentHeight\n        }\n\n    fun draw(canvas: Canvas, env: TypeEnvironment) {\n        env.clear()\n        for (line in mLines) {\n            line.draw(env, canvas)\n        }\n    }\n\n    private fun canInterrupt(element: Element): Boolean {\n        return mLines.size == getUsedMaxLine() &&\n                !calculateWholeLines &&\n                (ellipsize == null || ellipsize == TruncateAt.END) &&\n                element.next != null\n    }\n\n    private fun getUsedMaxLine(): Int {\n        return maxLines.coerceAtMost(exactlyHeightMaxLine)\n    }\n\n    fun release() {\n        for (line in mLines) {\n            line.release()\n        }\n        mLines.clear()\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/TypeEnvironment.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type\n\nimport android.content.res.Resources\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.Rect\nimport android.graphics.Typeface\nimport android.util.Log\nimport android.util.SparseArray\nimport java.util.*\n\nclass TypeEnvironment {\n    companion object {\n        private const val TAG = \"TypeEnvironment\"\n        const val TYPE_TEXT_COLOR = -1\n        const val TYPE_BG_COLOR = -2\n        const val TYPE_TYPEFACE = -3\n        const val TYPE_TEXT_SIZE = -4\n        const val TYPE_ALIGNMENT = -5\n        const val TYPE_LINE_SPACE = -6\n        const val TYPE_PARAGRAPH_SPACE = -7\n        const val TYPE_BORDER_TOP_WIDTH = -8\n        const val TYPE_BORDER_TOP_COLOR = -9\n        const val TYPE_BORDER_RIGHT_WIDTH = -10\n        const val TYPE_BORDER_RIGHT_COLOR = -11\n        const val TYPE_BORDER_BOTTOM_WIDTH = -12\n        const val TYPE_BORDER_BOTTOM_COLOR = -13\n        const val TYPE_BORDER_LEFT_WIDTH = -14\n        const val TYPE_BORDER_LEFT_COLOR = -15\n        const val TYPE_BORDER_PAINT = -16\n        const val TYPE_LINE_HEIGHT = -17\n\n        val DEFAULT_LAST_LINE_JUSTIFY_MAX_WIDTH = (Resources.getSystem().displayMetrics.density * 36).toInt()\n    }\n\n    enum class Alignment {\n        LEFT, RIGHT, CENTER, JUSTIFY\n    }\n\n    var widthLimit = 0\n        private set\n    var heightLimit = 0\n        private set\n\n\n    var alignment: Alignment = Alignment.JUSTIFY\n\n    var lastLineJustifyMaxWidth = DEFAULT_LAST_LINE_JUSTIFY_MAX_WIDTH\n\n    val paint = Paint().apply {\n        isAntiAlias = true\n        textSize = Resources.getSystem().displayMetrics.scaledDensity * 14f\n    }\n    val bgPaint = Paint().apply {\n        isAntiAlias = true\n    }\n    private val mCustomProp: SparseArray<Any?> = SparseArray()\n    private val mStack = SparseArray<Stack<Any?>>()\n\n    var workBreakMaxTryLength: Int = 10\n\n    var lineSpace = 0\n\n    var lineHeight = -1\n\n    var paragraphSpace: Int = 0\n        get() = field.coerceAtLeast(lineSpace)\n\n    var typeface: Typeface? = null\n        set(value) {\n            field = value\n            paint.typeface = value\n        }\n\n    var textSize: Float = paint.textSize\n        set(value) {\n            field = value\n            paint.textSize = value\n        }\n    var textColor: Int = Color.BLACK\n        set(value) {\n            field = value\n            paint.color = value\n        }\n    var backgroundColor: Int = Color.TRANSPARENT\n        set(value) {\n            field = value\n            bgPaint.color = value\n        }\n\n    fun setCustomProp(type: Int, value: Any?) {\n        mCustomProp.put(type, value)\n    }\n\n    fun setBorderTop(width: Int, color: Int) {\n        setCustomProp(TYPE_BORDER_TOP_WIDTH, width)\n        setCustomProp(TYPE_BORDER_TOP_COLOR, color)\n    }\n\n    val borderTopWidth: Int\n        get() = getIntCustomProp(TYPE_BORDER_TOP_WIDTH)\n    val borderTopColor: Int\n        get() = getIntCustomProp(TYPE_BORDER_TOP_COLOR)\n\n    fun setBorderRight(width: Int, color: Int) {\n        setCustomProp(TYPE_BORDER_RIGHT_WIDTH, width)\n        setCustomProp(TYPE_BORDER_RIGHT_COLOR, color)\n    }\n\n    val borderRightWidth: Int\n        get() = getIntCustomProp(TYPE_BORDER_RIGHT_WIDTH)\n    val borderRightColor: Int\n        get() = getIntCustomProp(TYPE_BORDER_RIGHT_COLOR)\n\n    fun setBorderBottom(width: Int, color: Int) {\n        setCustomProp(TYPE_BORDER_BOTTOM_WIDTH, width)\n        setCustomProp(TYPE_BORDER_BOTTOM_COLOR, color)\n    }\n\n    val borderBottomWidth: Int\n        get() = getIntCustomProp(TYPE_BORDER_BOTTOM_WIDTH)\n    val borderBottomColor: Int\n        get() = getIntCustomProp(TYPE_BORDER_BOTTOM_COLOR)\n\n    fun setBorderLeft(width: Int, color: Int) {\n        setCustomProp(TYPE_BORDER_LEFT_WIDTH, width)\n        setCustomProp(TYPE_BORDER_LEFT_COLOR, color)\n    }\n\n    val borderLeftWidth: Int\n        get() = getIntCustomProp(TYPE_BORDER_LEFT_WIDTH)\n    val borderLeftColor: Int\n        get() = getIntCustomProp(TYPE_BORDER_LEFT_COLOR)\n    val borderPaint: Paint\n        get() {\n            val obj = getCustomProp(TYPE_BORDER_PAINT)\n            val paint: Paint\n            if (obj == null) {\n                paint = Paint()\n                paint.isAntiAlias = true\n                setCustomProp(TYPE_BORDER_PAINT, paint)\n            } else {\n                paint = obj as Paint\n            }\n            return paint\n        }\n\n    fun getCustomProp(type: Int): Any? {\n        return mCustomProp[type]\n    }\n\n    fun getIntCustomProp(type: Int): Int {\n        val obj = mCustomProp[type]\n        return if (obj !is Int) {\n            0\n        } else obj\n    }\n\n    fun setMeasureLimit(widthLimit: Int, heightLimit: Int) {\n        this.widthLimit = widthLimit\n        this.heightLimit = heightLimit\n    }\n\n    fun snapshot(): TypeEnvironment {\n        val env = TypeEnvironment()\n        env.setMeasureLimit(widthLimit, heightLimit)\n        env.alignment = alignment\n        env.lineSpace = lineSpace\n        env.lineHeight = lineHeight\n        env.paragraphSpace = paragraphSpace\n        env.textSize = textSize\n        env.typeface = typeface\n        env.textColor = textColor\n        env.backgroundColor = backgroundColor\n        for (i in 0 until mStack.size()) {\n            env.mStack.put(mStack.keyAt(i), mStack.valueAt(i).clone() as Stack<Any?>)\n        }\n        for (i in 0 until mCustomProp.size()) {\n            env.setCustomProp(mCustomProp.keyAt(i), mCustomProp.valueAt(i))\n        }\n        return env\n    }\n\n    fun save(type: Int) {\n        var stack = mStack[type]\n        if (stack == null) {\n            stack = Stack()\n            mStack.put(type, stack)\n        }\n        if (type == TYPE_TEXT_COLOR) {\n            stack.push(textColor)\n        } else if (type == TYPE_BG_COLOR) {\n            stack.push(backgroundColor)\n        } else if (type == TYPE_TYPEFACE) {\n            stack.push(typeface)\n        } else if (type == TYPE_TEXT_SIZE) {\n            stack.push(textSize)\n        } else if (type == TYPE_ALIGNMENT) {\n            stack.push(alignment)\n        } else if (type == TYPE_LINE_SPACE) {\n            stack.push(lineSpace)\n        } else if (type == TYPE_PARAGRAPH_SPACE) {\n            stack.push(paragraphSpace)\n        } else if(type == TYPE_LINE_HEIGHT){\n            stack.push(lineHeight)\n        } else{\n            stack.push(mCustomProp[type])\n        }\n    }\n\n    fun restore(type: Int) {\n        val stack = mStack[type]\n        if (stack == null || stack.isEmpty()) {\n            Log.d(TAG, \"restore (type = $type)with a empty stack.\")\n            return\n        }\n        val v = stack.pop()\n        restore(type, v)\n    }\n\n    private fun restore(type: Int, v: Any?) {\n        if (type == TYPE_TEXT_COLOR) {\n            textColor = v as Int\n        } else if (type == TYPE_BG_COLOR) {\n            backgroundColor = v as Int\n        } else if (type == TYPE_TYPEFACE) {\n            typeface = v as? Typeface\n        } else if (type == TYPE_TEXT_SIZE) {\n            textSize = v as Float\n        } else if (type == TYPE_ALIGNMENT) {\n            alignment = v as Alignment\n        } else if (type == TYPE_LINE_SPACE) {\n            lineSpace = v as Int\n        } else if (type == TYPE_PARAGRAPH_SPACE) {\n            paragraphSpace = v as Int\n        }else if (type == TYPE_LINE_HEIGHT) {\n            lineHeight = v as Int\n        } else {\n            setCustomProp(type, v)\n        }\n    }\n\n    fun clear() {\n        for (i in 0 until mStack.size()) {\n            val stack = mStack.valueAt(i)\n            if (stack != null && stack.size > 0) {\n                while (stack.size > 1) {\n                    stack.pop()\n                }\n                restore(mStack.keyAt(i), stack.pop())\n            }\n        }\n    }\n\n    fun isRunning(): Boolean{\n        for (i in 0 until mStack.size()) {\n            val stack = mStack.valueAt(i)\n            if(stack.size > 0){\n                return true\n            }\n        }\n        return false\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/TypeModel.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type\n\nimport android.graphics.Typeface\nimport com.qmuiteam.qmui.type.element.Element\n\nclass TypeModel(\n    val origin: CharSequence,\n    private val mElementMap: Map<Int, Element>,\n    private val mFirstElement: Element,\n    private val mLastElement: Element\n) {\n\n    var firstEffect: Element? = null\n\n    fun addTypefaceEffect(start: Int, end: Int, typeface: Typeface): EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_TYPEFACE)\n        return unsafeAddEffect(start, end, types) { env -> env.typeface = typeface }\n    }\n\n    fun addTextSizeEffect(start: Int, end: Int, textSize: Float): EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_TEXT_SIZE)\n        return unsafeAddEffect(start, end, types) { env -> env.textSize = textSize }\n    }\n\n    fun addBgEffect(start: Int, end: Int, bgColor: Int): EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_BG_COLOR)\n        return unsafeAddEffect(start, end, types) { env -> env.backgroundColor = bgColor }\n    }\n\n    fun addTextColorEffect(start: Int, end: Int, textColor: Int): EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_TEXT_COLOR)\n        return unsafeAddEffect(start, end, types) { env -> env.textColor = textColor }\n    }\n\n    fun addUnderLineEffect(start: Int, end: Int, underLineColor: Int, underLineHeight: Int): EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH)\n        types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR)\n        return unsafeAddEffect(\n            start, end, types\n        ) { env -> env.setBorderBottom(underLineHeight, underLineColor) }\n    }\n\n    fun unsafeAddEffect(start: Int, end: Int, types: List<Int>, environmentUpdater: EnvironmentUpdater): EffectRemover? {\n        if (start > end) {\n            throw RuntimeException(\"unsafeAddEffect: start($start) is bigger than end($end)\")\n        }\n        val elementStart = getByPos(start)\n        val elementEnd = getByPos(end)\n        if (elementStart == null || elementEnd == null) {\n            return null\n        }\n        for (type in types) {\n            elementStart.addSaveType(type)\n            elementEnd.addRestoreType(type)\n        }\n        elementStart.addEnvironmentUpdater(environmentUpdater)\n        firstEffect = if (firstEffect == null) {\n            elementStart\n        } else {\n            elementStart.insertEffectTo(firstEffect!!)\n        }\n        firstEffect = elementEnd.insertEffectTo(firstEffect!!)\n        return DefaultEffectRemove(this, start, end, types, environmentUpdater)\n    }\n\n    fun unsafeRemoveEffect(start: Int, end: Int, types: List<Int>, environmentUpdater: EnvironmentUpdater): Boolean {\n        val elementStart = getByPos(start)\n        val elementEnd = getByPos(end)\n        if (elementStart == null || elementEnd == null) {\n            return false\n        }\n        for (type in types) {\n            elementStart.removeSaveType(type)\n            elementEnd.removeStoreType(type)\n        }\n        elementStart.removeEnvironmentUpdater(environmentUpdater)\n        firstEffect = elementStart.removeFromEffectListIfNeeded(firstEffect)\n        firstEffect = elementEnd.removeFromEffectListIfNeeded(firstEffect)\n        return true\n    }\n\n    fun firstElement(): Element {\n        return mFirstElement\n    }\n\n    fun lastElement(): Element {\n        return mLastElement\n    }\n\n\n    fun getByPos(pos: Int): Element? {\n        val anchor = mElementMap[pos] ?: mLastElement\n        val anchorEnd = anchor.start + anchor.text.length\n        if (anchor.start <= pos && anchorEnd > pos) {\n            return anchor\n        } else if (anchorEnd <= pos) {\n            var next = anchor.next\n            while (next != null) {\n                if (next.start + next.text.length > pos) {\n                    return next\n                }\n                next = next.next\n            }\n            return null\n        } else {\n            var prev = anchor.prev\n            while (prev != null) {\n                if (prev.start <= pos) {\n                    return prev\n                }\n                prev = prev.prev\n            }\n            return null\n        }\n    }\n\n    fun getByIndex(pos: Int): Element? {\n        return mElementMap[pos]\n    }\n\n    fun interface EffectRemover {\n        fun remove()\n    }\n}\n\nclass DefaultEffectRemove(\n    private val typeModel: TypeModel,\n    private val start: Int,\n    private val end: Int,\n    private val types: List<Int>,\n    private val environmentUpdater: EnvironmentUpdater\n) : TypeModel.EffectRemover {\n    override fun remove() {\n        typeModel.unsafeRemoveEffect(start, end, types, environmentUpdater)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/BreakWordLineElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nclass BreakWordLineElement : TextElement(\"-\", -1, -1) {\n    init {\n        wordPart = WORD_PART_MIDDLE\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/DrawableElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport android.graphics.drawable.Drawable\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nclass DrawableElement(\n        drawable: Drawable,\n        text: CharSequence,\n        index: Int, start: Int) : Element(text, index, start) {\n\n    val drawable = drawable.mutate().apply {\n        setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)\n    }\n\n    override fun onMeasure(env: TypeEnvironment) {\n        setMeasureDimen(drawable.intrinsicWidth, drawable.intrinsicHeight, 0)\n    }\n\n    override fun onDraw(env: TypeEnvironment, canvas: Canvas) {\n        drawBg(env, canvas)\n        canvas.save()\n        canvas.translate(x.toFloat(), y.toFloat())\n        drawable.draw(canvas)\n        canvas.restore()\n        drawBorder(env, canvas)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/Element.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport com.qmuiteam.qmui.type.EnvironmentUpdater\nimport com.qmuiteam.qmui.type.TypeEnvironment\nimport java.util.*\nimport kotlin.collections.ArrayList\n\nabstract class Element(val text: CharSequence, val index: Int, val start: Int) {\n    companion object {\n        const val VISIBLE = 0\n        const val GONE = 1\n        const val WORD_PART_WHOLE = 0\n        const val WORD_PART_START = 1\n        const val WORD_PART_MIDDLE = 2\n        const val WORD_PART_END = 3\n        const val LINE_BREAK_TYPE_NORMAL = 0\n        const val LINE_BREAK_TYPE_NOT_START = 1\n        const val LINE_BREAK_TYPE_NOT_END = 2\n        const val LINE_BREAK_WORD_BREAK_ALLOWED = 3\n        private val NOT_START_CHARS = charArrayOf(\n                ',', '.', ';', ']', '>', ')', '?', '\"', '\\'', '!', ':', '}', '」',\n                '，', '。', '；', '、', '】', '》', '）', '？', '”', '！', '：', '』')\n        private val NOT_END_CHARS = charArrayOf(\n                '(', '<', '[', '{', '“', '「', '『', '（', '《'\n        )\n\n        init {\n            Arrays.sort(NOT_START_CHARS)\n            Arrays.sort(NOT_END_CHARS)\n        }\n    }\n\n    var prevEffect: Element? = null\n        private set\n    var nextEffect: Element? = null\n        private set\n\n    var wordPart = WORD_PART_WHOLE\n    var lineBreakType = LINE_BREAK_TYPE_NORMAL\n    var visible = VISIBLE\n\n\n    var measureWidth = 0\n        private set\n    var measureHeight = 0\n        private set\n    var x = 0\n    var y = 0\n    var baseLine = 0\n    var nextGapWidth = 0\n\n    private var saveTypeList: MutableList<Int>? = null\n    private var restoreTypeList: MutableList<Int>? = null\n    private var environmentUpdaterList: MutableList<EnvironmentUpdater>? = null\n\n    val length: Int = text.length\n\n    private var _prev: Element? = null\n    private var _next: Element? = null\n\n    var next: Element?\n        get() = _next\n        set(element) {\n            _next = element\n            if (element != null) {\n                element._prev = this\n            }\n        }\n    var prev: Element?\n        get() = _prev\n        set(element) {\n            _prev = element\n            if (element != null) {\n                element._next = this\n            }\n        }\n\n    private val rightWithGap: Int\n        get() = x + measureWidth + nextGapWidth\n\n    init {\n        if(text.length == 1){\n            if (Arrays.binarySearch(NOT_START_CHARS, text[0]) >= 0) {\n                lineBreakType = LINE_BREAK_TYPE_NOT_START\n            } else if (Arrays.binarySearch(NOT_END_CHARS, text[0]) >= 0) {\n                lineBreakType = LINE_BREAK_TYPE_NOT_END\n            }\n        }\n    }\n\n    fun insertEffectTo(head: Element): Element {\n        if (head === this) {\n            return head\n        }\n        if (index < head.index) {\n            head.prevEffect = this\n            nextEffect = head\n            return this\n        }\n        var current: Element = head\n        var next = head.nextEffect\n        while (next != null) {\n            if (next === this) {\n                // already in list\n                return head\n            }\n            if (next.index > index) {\n                current.nextEffect = this\n                next.prevEffect = this\n                prevEffect = current\n                nextEffect = next\n                return head\n            }\n            current = next\n            next = next.nextEffect\n        }\n        current.nextEffect = this\n        prevEffect = current\n        nextEffect = null\n        return head\n    }\n\n    fun removeFromEffectListIfNeeded(head: Element?): Element? {\n        if(head == null){\n            return null\n        }\n        val noSaveType = saveTypeList.isNullOrEmpty()\n        val noRestoreType = restoreTypeList.isNullOrEmpty()\n        if (noSaveType && noRestoreType) {\n            val prev = prevEffect\n            val next = nextEffect\n            if (prev != null) {\n                prev.nextEffect = next\n                prevEffect = null\n            }\n            if (next != null) {\n                next.prevEffect = prev\n                nextEffect = null\n            }\n            if (head === this) {\n                return next\n            }\n        }\n        return head\n    }\n\n    fun addSaveType(type: Int) {\n        val list = saveTypeList ?:  ArrayList<Int>().also { saveTypeList = it}\n        list.add(type)\n    }\n\n    fun removeSaveType(type: Int) {\n        val list = saveTypeList ?: return\n        for (i in list.indices) {\n            if (list[i] == type) {\n                list.removeAt(i)\n                break\n            }\n        }\n    }\n\n    fun addRestoreType(type: Int) {\n        val list = restoreTypeList ?:  ArrayList<Int>().also { restoreTypeList = it}\n        list.add(type)\n    }\n\n    fun removeStoreType(type: Int) {\n        val list = restoreTypeList ?: return\n        for (i in list.indices) {\n            if (list[i] == type) {\n                list.removeAt(i)\n                break\n            }\n        }\n    }\n\n    fun hasEnvironmentUpdater(): Boolean {\n        return (environmentUpdaterList?.size ?: 0) > 0\n    }\n\n    fun hasSaveType(): Boolean {\n        return !saveTypeList.isNullOrEmpty()\n    }\n\n    fun hasRestoreType(): Boolean {\n        return !restoreTypeList.isNullOrEmpty()\n    }\n\n    fun addEnvironmentUpdater(environmentUpdater: EnvironmentUpdater) {\n        val list = environmentUpdaterList ?: ArrayList<EnvironmentUpdater>().also { environmentUpdaterList = it }\n        list.add(environmentUpdater)\n    }\n\n    fun removeEnvironmentUpdater(environmentUpdater: EnvironmentUpdater) {\n        val list = environmentUpdaterList ?: return\n        list.remove(environmentUpdater)\n    }\n\n    fun addSingleEnvironmentUpdater(changedTypes: List<Int>?, environmentUpdater: EnvironmentUpdater) {\n        if (changedTypes != null) {\n            for (type in changedTypes) {\n                addSaveType(type)\n                addRestoreType(type)\n            }\n        }\n        addEnvironmentUpdater(environmentUpdater)\n    }\n\n    fun move(environment: TypeEnvironment) {\n        if (hasEnvironmentUpdater()) {\n            updateEnv(environment)\n            restoreEnv(environment)\n        }\n    }\n\n    override fun toString(): String {\n        return text.toString()\n    }\n\n\n    protected fun setMeasureDimen(measureWidth: Int, measureHeight: Int, baseline: Int) {\n        this.measureWidth = measureWidth\n        this.measureHeight = measureHeight\n        baseLine = baseline\n    }\n\n\n    fun measure(env: TypeEnvironment) {\n        updateEnv(env)\n        onMeasure(env)\n        restoreEnv(env)\n    }\n\n    fun draw(env: TypeEnvironment, canvas: Canvas) {\n        updateEnv(env)\n        if (visible == VISIBLE) {\n            onDraw(env, canvas)\n        }\n        restoreEnv(env)\n    }\n\n    fun updateEnv(env: TypeEnvironment) {\n        saveTypeList?.forEach {\n            env.save(it)\n        }\n        environmentUpdaterList?.forEach {\n            it.update(env)\n        }\n    }\n\n    fun restoreEnv(env: TypeEnvironment) {\n        restoreTypeList?.forEach {\n            env.restore(it)\n        }\n    }\n\n    protected abstract fun onMeasure(env: TypeEnvironment)\n    protected abstract fun onDraw(env: TypeEnvironment, canvas: Canvas)\n    protected fun drawBg(env: TypeEnvironment, canvas: Canvas) {\n        if (env.backgroundColor != Color.TRANSPARENT) {\n            canvas.drawRect(x.toFloat(), y.toFloat(), rightWithGap.toFloat(), (y + measureHeight).toFloat(), env.bgPaint)\n        }\n    }\n\n    protected fun drawBorder(env: TypeEnvironment, canvas: Canvas) {\n        val paint = env.borderPaint\n        if (env.borderLeftWidth > 0) {\n            paint.color = env.borderLeftColor\n            canvas.drawRect(x.toFloat(), y.toFloat(), (x + env.borderLeftWidth).toFloat(), (y + measureHeight).toFloat(), paint)\n        }\n        if (env.borderTopWidth > 0) {\n            paint.color = env.borderTopColor\n            canvas.drawRect(x.toFloat(), y.toFloat(), rightWithGap.toFloat(), (y + env.borderTopWidth).toFloat(), paint)\n        }\n        if (env.borderRightWidth > 0) {\n            paint.color = env.borderRightColor\n            canvas.drawRect((x + measureWidth - env.borderRightWidth).toFloat(), y.toFloat(),\n                    (x + measureWidth).toFloat(), (y + measureHeight).toFloat(), paint)\n        }\n        if (env.borderBottomWidth > 0) {\n            paint.color = env.borderBottomColor\n            canvas.drawRect(x.toFloat(), (y + measureHeight - env.borderBottomWidth).toFloat(),\n                    rightWithGap.toFloat(), (y + measureHeight).toFloat(), paint)\n        }\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/EmojiElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport android.graphics.drawable.Drawable\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nclass EmojiElement(val drawable: Drawable, text: CharSequence, index: Int, start: Int) : Element(text, index, start) {\n\n    override fun onMeasure(env: TypeEnvironment) {\n        val paint = env.paint\n        val size = (paint.fontMetrics.descent - paint.fontMetrics.ascent).toInt()\n        drawable.setBounds(0, 0, size, size)\n        setMeasureDimen(size, size, 0)\n    }\n\n    override fun onDraw(env: TypeEnvironment, canvas: Canvas) {\n        drawBg(env, canvas)\n        canvas.save()\n        canvas.translate(x.toFloat(), y.toFloat())\n        drawable.draw(canvas)\n        canvas.restore()\n        drawBorder(env, canvas)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/IgnoreEffectElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport com.qmuiteam.qmui.type.EnvironmentUpdater\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nclass IgnoreEffectElement(list: List<Element>) : Element(\"\", -1, -1) {\n\n    init {\n        addEnvironmentUpdater(object: EnvironmentUpdater {\n            override fun update(env: TypeEnvironment) {\n                for (element in list) {\n                    element.move(env)\n                }\n            }\n\n        })\n    }\n\n    override fun onMeasure(env: TypeEnvironment) {\n        setMeasureDimen(0, 0, 0)\n    }\n\n    override fun onDraw(env: TypeEnvironment, canvas: Canvas) {}\n\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/NextParagraphElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nclass NextParagraphElement(text: CharSequence, index: Int, start: Int) : Element(text, index, start) {\n\n    override fun onMeasure(env: TypeEnvironment) {\n        setMeasureDimen(0, 0, 0)\n    }\n\n    override fun onDraw(env: TypeEnvironment, canvas: Canvas) {}\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/element/TextElement.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.element\n\nimport android.graphics.Canvas\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nopen class TextElement(text: CharSequence, index: Int, start: Int) : Element(text, index, start) {\n\n    override fun onMeasure(env: TypeEnvironment) {\n        val paint = env.paint\n        setMeasureDimen((paint.measureText(text, 0, text.length) + 0.5f).toInt(),\n                paint.fontMetricsInt.descent - paint.fontMetricsInt.ascent,\n                -paint.fontMetricsInt.ascent)\n    }\n\n    override fun onDraw(env: TypeEnvironment, canvas: Canvas) {\n        drawBg(env, canvas)\n        canvas.drawText(text, 0, text.length, x.toFloat(), (y + baseLine).toFloat(), env.paint)\n        drawBorder(env, canvas)\n    }\n}\n\nclass MoreTextElement(text: CharSequence, index: Int, start: Int): TextElement(text, index, start)"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/emoji/EmojiModel.kt",
    "content": "package com.qmuiteam.qmui.type.emoji\n\nimport android.text.SpannableString\nimport android.text.Spanned\nimport android.util.SparseArray\nimport androidx.core.util.putAll\nimport com.qmuiteam.qmui.type.TypeModel\nimport com.qmuiteam.qmui.type.element.Element\nimport com.qmuiteam.qmui.type.element.EmojiElement\n\nclass Emoji(val span: EmojiSpan, val text: CharSequence, var start: Int) {\n    var prev: Emoji? = null\n    var next: Emoji? = null\n}\n\nclass EmojiModel(\n    val map: SparseArray<Emoji>,\n    var begin: Emoji,\n    var end: Emoji\n) {\n    fun merge(other: EmojiModel) {\n        map.putAll(other.map)\n        when {\n            other.begin.start > end.start -> {\n                end.next = other.begin\n                other.begin.prev = end\n                end = other.end\n            }\n            other.end.start < begin.start -> {\n                other.end.next = begin\n                begin.prev = other.end\n                begin = other.begin\n            }\n            else -> {\n                var next: Emoji?\n                var otherNext: Emoji?\n                val newBegin: Emoji = if (begin.start < other.begin.start) {\n                    next = begin.next\n                    otherNext = other.begin\n                    begin\n                } else {\n                    otherNext = other.begin.next\n                    next = begin\n                    other.begin\n                }\n                var cur = newBegin\n                while (next != null || otherNext != null) {\n                    when {\n                        next == null -> {\n                            cur.next = otherNext\n                            otherNext!!.prev = cur\n                            begin = newBegin\n                            end = other.end\n                            return\n                        }\n                        otherNext == null -> {\n                            cur.next = next\n                            next.prev = cur\n                            begin = newBegin\n                            return\n                        }\n                        next.start < otherNext.start -> {\n                            cur.next = next\n                            next.prev = cur\n                            cur = next\n                            next = next.next\n                        }\n                        else -> {\n                            cur.next = otherNext\n                            otherNext.prev = cur\n                            cur = otherNext\n                            otherNext = otherNext.next\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    fun removeEmoji(emoji: Emoji): Boolean{\n        map.remove(emoji.start)\n        if(emoji == begin){\n            begin = emoji.next ?: return true\n            begin.prev = null\n            return false\n        }\n\n        if(emoji == end){\n            end = emoji.prev ?: return true\n            end.next = null\n            return false\n        }\n        val prev = emoji.prev\n        val next = emoji.next\n        prev?.next = next\n        next?.prev = prev\n        return false\n    }\n\n    fun getEmoji(pos: Int): Emoji? {\n        if (begin.start > pos) {\n            return null\n        }\n        if (end.start + end.text.length <= pos) {\n            return null\n        }\n        var lo = 0\n        var hi: Int = map.size() - 1\n\n        while (lo <= hi) {\n            val mid = lo + hi ushr 1\n            val midVal = map.valueAt(mid)\n            when {\n                midVal.start + midVal.text.length <= pos -> {\n                    lo = mid + 1\n                }\n                midVal.start > pos -> {\n                    hi = mid - 1\n                }\n                else -> {\n                    return midVal\n                }\n            }\n        }\n        return null\n\n    }\n}\n\nfun TypeModel.toEmojiModel(offset: Int, emojiSizeGetter: () -> Int): EmojiModel? {\n    var node: Element? = firstElement()\n    val map = SparseArray<Emoji>()\n    var begin: Emoji? = null\n    var end: Emoji? = null\n    while (node != null) {\n        if (node is EmojiElement) {\n            val next = Emoji(EmojiSpan(node.drawable.apply {\n                setBounds(0, 0, intrinsicWidth, intrinsicHeight)\n            }, emojiSizeGetter), node.text, offset + node.start)\n            map.put(offset + node.start, next)\n            if (begin == null) {\n                begin = next\n                end = next\n            } else {\n                next.prev = end\n                end!!.next = next\n                end = next\n            }\n        }\n        node = node.next\n    }\n    if (begin == null) {\n        return null\n    }\n    return EmojiModel(map, begin, end!!)\n}\n\nfun TypeModel.toSpannableString(emojiSizeGetter: () -> Int): SpannableString{\n    val ss = (origin as? SpannableString)?.apply {\n        getSpans(0, length, EmojiSpan::class.java)?.forEach {  span ->\n            removeSpan(span)\n        }\n    } ?:  SpannableString(origin)\n    var cur: Element? = firstElement()\n    while (cur != null){\n        if(cur is EmojiElement){\n            ss.setSpan(\n                EmojiSpan(cur.drawable, emojiSizeGetter),\n                cur.start,\n                cur.start + cur.text.length,\n                Spanned.SPAN_INCLUSIVE_EXCLUSIVE\n            )\n        }\n        cur = cur.next\n    }\n    return ss\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/emoji/EmojiSpan.kt",
    "content": "package com.qmuiteam.qmui.type.emoji\n\nimport android.graphics.Canvas\nimport android.graphics.Paint\nimport android.graphics.drawable.Drawable\nimport android.text.style.ImageSpan\nimport com.qmuiteam.qmui.type.view.EmojiEditText\n\nclass EmojiSpan(drawable: Drawable, val emojiSizeGetter: () -> Int) : ImageSpan(drawable) {\n    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {\n        val emojiSize = emojiSizeGetter()\n        val rect = drawable.bounds\n        rect.left = 0\n        rect.top = 0\n        rect.right = if(emojiSize == EmojiEditText.EmojiOriginSize) drawable.intrinsicWidth else emojiSize\n        rect.bottom = if(emojiSize == EmojiEditText.EmojiOriginSize) drawable.intrinsicHeight else emojiSize\n        drawable.bounds = rect\n        if (fm != null) {\n            val fontMetricsHeight = fm.descent - fm.ascent\n            if(fontMetricsHeight < rect.bottom){\n                val ratio = rect.bottom.toFloat() / fontMetricsHeight.toFloat()\n                fm.ascent = (fm.ascent * ratio).toInt()\n                fm.descent = (fm.descent * ratio).toInt()\n                fm.top = fm.ascent\n                fm.bottom = fm.descent\n            }\n        }\n\n        return rect.right\n    }\n\n    override fun draw(\n        canvas: Canvas, text: CharSequence?, start: Int, end: Int,\n        x: Float, top: Int, y: Int, bottom: Int, paint: Paint\n    ) {\n        canvas.save()\n        val rect = drawable.bounds\n        val fontMetricsInt = paint.fontMetricsInt\n        val fontTop = y + fontMetricsInt.top\n        val fontMetricsHeight = fontMetricsInt.bottom - fontMetricsInt.top\n        val iconHeight = rect.height()\n        val iconTop = fontTop + (fontMetricsHeight - iconHeight) / 2\n        canvas.translate(x, iconTop.toFloat())\n        drawable.draw(canvas)\n        canvas.restore()\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiResourceProvider.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.parser\n\nimport android.graphics.drawable.Drawable\n\ninterface EmojiResourceProvider {\n    fun queryForDrawable(text: CharSequence): Drawable?\n    fun queryForDrawable(c: Char): Drawable?\n    fun queryForDrawable(codePoint: Int): Drawable?\n    fun queryForDrawable(firstCodePoint: Int, secondCodePint: Int): Drawable?\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/parser/EmojiTextParser.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.parser\n\nimport com.qmuiteam.qmui.type.TypeModel\nimport com.qmuiteam.qmui.type.element.Element\nimport com.qmuiteam.qmui.type.element.EmojiElement\nimport com.qmuiteam.qmui.type.element.NextParagraphElement\nimport com.qmuiteam.qmui.type.element.TextElement\nimport java.util.*\n\nclass EmojiTextParser(\n    private val emojiProvider: EmojiResourceProvider,\n    private val wordBreakChecker: (c: Char) -> Boolean\n) : TextParser {\n\n    override fun parse(text: CharSequence?): TypeModel? {\n        if(text == null || text.isEmpty()){\n            return null\n        }\n\n        val size = text.length\n        val map = HashMap<Int, Element>(size)\n        var first: Element? = null\n        var last: Element? = null\n        var tmp: Element? = null\n        var index = 0\n        var i = 0\n        while (i < size) {\n            val c = text[i]\n            if (c == '\\n') {\n                tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)\n            } else if (c == '\\r') {\n                if (i + 1 < text.length && text[i + 1] == '\\n') {\n                    tmp = NextParagraphElement(text.subSequence(i, i + 2), index, i)\n                    i++\n                } else {\n                    tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)\n                }\n            } else if (c == '[') {\n                var j = i + 1\n                var find = false\n                val end = Math.min(i + 30, size)\n                while (j < end) {\n                    if (text[j] == ']') {\n                        val sub = text.subSequence(i, j + 1)\n                        val emoji = emojiProvider.queryForDrawable(sub)\n                        if (emoji != null) {\n                            tmp = EmojiElement(emoji, text.subSequence(i, j + 1), index, i)\n                            i = j\n                            find = true\n                            break\n                        }\n                    }\n                    j++\n                }\n                if (!find) {\n                    val unicode = Character.codePointAt(text, i)\n                    val charCount = Character.charCount(unicode)\n                    tmp = TextElement(text.subSequence(i, i + charCount), index, i)\n                    i += charCount - 1\n                }\n            } else {\n                var handled = false\n                var emoji = emojiProvider.queryForDrawable(c)\n                if (emoji != null) {\n                    handled = true\n                    tmp = EmojiElement(emoji, text.subSequence(i, i + 1), index, i)\n                }\n                if (!handled) {\n                    val unicode = Character.codePointAt(text, i)\n                    val codeCount = Character.charCount(unicode)\n                    emoji = emojiProvider.queryForDrawable(unicode)\n                    if (emoji != null) {\n                        handled = true\n                        tmp = EmojiElement(emoji, text.subSequence(i, i + codeCount), index, i)\n                        i += codeCount - 1\n                    }\n                    val nextStart = i + codeCount\n                    if (!handled && nextStart < size) {\n                        val nextUnicode = Character.codePointAt(text, nextStart)\n                        emoji = emojiProvider.queryForDrawable(unicode, nextUnicode)\n                        if (emoji != null) {\n                            handled = true\n                            val nextCodeCount = Character.charCount(nextUnicode)\n                            tmp = EmojiElement(emoji, text.subSequence(i, nextStart + nextCodeCount), index, i)\n                            i = nextStart + nextCodeCount - 1\n                        }\n                    }\n                }\n                if (!handled) {\n                    val charCount = ParserHelper.handleUnionIfNeeded(text, i)\n                    tmp = TextElement(text.subSequence(i, i + charCount), index, i)\n                    i += charCount - 1\n                }\n            }\n            ParserHelper.handleWordPart(c, last, tmp!!, wordBreakChecker)\n            index++\n            if (first == null) {\n                first = tmp\n                last = tmp\n            } else {\n                last!!.next = tmp\n                last = tmp\n            }\n            map[tmp.index] = tmp\n            i++\n        }\n        return TypeModel(text, map, first!!, last!!)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/parser/ParserHelper.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.parser\n\nimport com.qmuiteam.qmui.type.element.Element\n\nobject ParserHelper {\n\n    fun isEnglishLetterOrNumber(c: Char): Boolean {\n        return c in 'a'..'z' || c in 'A'..'Z' || c in '0'..'9'\n    }\n\n    fun handleWordPart(c: Char, prev: Element?, curr: Element, wordBreakChecker: (c: Char) -> Boolean) {\n        if (isEnglishLetterOrNumber(c)) {\n            if (prev == null || prev.wordPart == Element.WORD_PART_WHOLE || prev.wordPart == Element.WORD_PART_END) {\n                curr.wordPart = Element.WORD_PART_START\n            } else {\n                curr.wordPart = Element.WORD_PART_MIDDLE\n                if (wordBreakChecker(c)) {\n                    curr.lineBreakType = Element.LINE_BREAK_WORD_BREAK_ALLOWED\n                }\n            }\n        } else {\n            if (prev != null && prev.wordPart == Element.WORD_PART_MIDDLE) {\n                prev.wordPart = Element.WORD_PART_END\n                prev.lineBreakType = Element.LINE_BREAK_TYPE_NORMAL\n            }\n            curr.wordPart = Element.WORD_PART_WHOLE\n        }\n    }\n\n    fun handleUnionIfNeeded(text: CharSequence, i: Int): Int {\n        val unicode = Character.codePointAt(text, i)\n        var charCount = Character.charCount(unicode)\n        var next = i + charCount\n        while (next < text.length) {\n            val nextUnicode = Character.codePointAt(text, next)\n            val type = Character.getType(nextUnicode)\n            if (type == Character.NON_SPACING_MARK.toInt()) {\n                val nextCharCount = Character.charCount(nextUnicode)\n                charCount += nextCharCount\n                next += nextCharCount\n            } else {\n                break\n            }\n        }\n        return charCount\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/parser/PlainTextParser.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.parser\n\nimport com.qmuiteam.qmui.type.TypeModel\nimport com.qmuiteam.qmui.type.element.Element\nimport com.qmuiteam.qmui.type.element.NextParagraphElement\nimport com.qmuiteam.qmui.type.element.TextElement\nimport com.qmuiteam.qmui.type.parser.ParserHelper.handleWordPart\nimport java.util.*\n\nclass PlainTextParser(\n    private val wordBreakChecker: (c: Char) -> Boolean\n) : TextParser {\n\n    companion object {\n        val instance by lazy {\n            PlainTextParser {\n                false\n            }\n        }\n    }\n\n    override fun parse(text: CharSequence?): TypeModel? {\n        if (text == null || text.isEmpty()) {\n            return null\n        }\n        val size = text.length\n        val map = HashMap<Int, Element>(size)\n        var first: Element? = null\n        var last: Element? = null\n        var tmp: Element\n        var index = 0\n        var i = 0\n        while (i < size) {\n            val c = text[i]\n            if (c == '\\n') {\n                tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)\n            } else if (c == '\\r') {\n                if (i + 1 < text.length && text[i + 1] == '\\n') {\n                    tmp = NextParagraphElement(text.subSequence(i, i + 2), index, i)\n                    i++\n                } else {\n                    tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)\n                }\n            } else {\n                val charCount = ParserHelper.handleUnionIfNeeded(text, i)\n                tmp = TextElement(text.subSequence(i, i + charCount), index, i)\n                i += charCount - 1\n            }\n            handleWordPart(c, last, tmp, wordBreakChecker)\n            index++\n            if (first == null) {\n                first = tmp\n                last = tmp\n            } else {\n                last!!.next = tmp\n                last = tmp\n            }\n            map[tmp.index] = tmp\n            i++\n        }\n        return TypeModel(text, map, first!!, last!!)\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/parser/TextParser.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.qmuiteam.qmui.type.parser\n\nimport com.qmuiteam.qmui.type.TypeModel\n\ninterface TextParser {\n    fun parse(text: CharSequence?): TypeModel?\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/view/BaseTypeView.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.type.view\n\nimport android.content.Context\nimport android.graphics.Typeface\nimport android.util.AttributeSet\nimport android.view.View\nimport com.qmuiteam.qmui.type.TypeEnvironment\n\nopen class BaseTypeView(context: Context,\n                        attrs: AttributeSet? = null,\n                        defStyleAttr: Int = 0): View(context, attrs, defStyleAttr) {\n    val environment = TypeEnvironment()\n\n    var textSize: Float\n        get() = environment.textSize\n        set(value){\n            throwIfRunning(\"setTextSize\")\n            if(environment.textSize != value){\n                environment.textSize = value\n                requestLayout()\n            }\n        }\n\n    var textColor: Int\n        get() = environment.textColor\n        set(value){\n            throwIfRunning(\"setTextColor\")\n            if(environment.textColor != value){\n                environment.textColor = value\n                invalidate()\n            }\n        }\n\n    var typeface: Typeface?\n        get() = environment.typeface\n        set(value){\n            throwIfRunning(\"setTypeface\")\n            if(environment.typeface != value){\n                environment.typeface = value\n                requestLayout()\n            }\n        }\n\n    var lineSpace: Int\n        get() = environment.lineSpace\n        set(value){\n            throwIfRunning(\"setLineSpace\")\n            if(environment.lineSpace != value){\n                environment.lineSpace = value\n                requestLayout()\n            }\n        }\n\n    var lineHeight: Int\n        get() = environment.lineHeight\n        set(value){\n            throwIfRunning(\"setLineHeight\")\n            if(environment.lineHeight != value){\n                environment.lineHeight = value\n                requestLayout()\n            }\n        }\n\n    var paragraphSpace: Int\n        get() = environment.paragraphSpace\n        set(value){\n            throwIfRunning(\"setParagraphSpace\")\n            if(environment.paragraphSpace != value){\n                environment.paragraphSpace = value\n                requestLayout()\n            }\n        }\n\n\n    fun throwIfRunning(action: String) {\n        if(environment.isRunning()){\n            throw RuntimeException(\"can not perform $action when running.\")\n        }\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/view/EmojiEditText.kt",
    "content": "package com.qmuiteam.qmui.type.view\n\nimport android.content.Context\nimport android.text.Editable\nimport android.text.Spannable\nimport android.text.SpannableString\nimport android.text.TextWatcher\nimport android.util.AttributeSet\nimport android.util.Log\nimport androidx.appcompat.widget.AppCompatEditText\nimport androidx.core.util.valueIterator\nimport com.qmuiteam.qmui.type.emoji.Emoji\nimport com.qmuiteam.qmui.type.emoji.EmojiModel\nimport com.qmuiteam.qmui.type.emoji.EmojiSpan\nimport com.qmuiteam.qmui.type.emoji.toEmojiModel\nimport com.qmuiteam.qmui.type.parser.EmojiTextParser\n\nopen class EmojiEditText(\n    context: Context,\n    attributeSet: AttributeSet? = null\n) : AppCompatEditText(context, attributeSet) {\n\n    companion object {\n        const val EmojiOriginSize = -1\n    }\n\n\n    private var emojiModel: EmojiModel? = null\n    private var isTextFirstSet: Boolean = false\n    private var isTextManualSetting: Boolean = false\n\n    var emojiSize: Int = EmojiOriginSize\n        set(value) {\n            if (field != value) {\n                field = value\n                invalidate()\n            }\n        }\n\n    var textParser: EmojiTextParser? = null\n        set(value) {\n            if (field != value) {\n                field = value\n                if (isTextFirstSet) {\n                    setText(text, BufferType.EDITABLE)\n                }\n            }\n        }\n\n    private val textWatcher = object : TextWatcher {\n\n        private val pendingRemoveRange = mutableListOf<Pair<Int, Int>>()\n        private var isPendingRemoving = false\n        private val correctingEmoji = ArrayList<Emoji>()\n\n        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {\n            if (isPendingRemoving || isTextManualSetting) {\n                return\n            }\n            val model = emojiModel\n            if (s != null && model != null && count > 0) {\n                val end = start + count\n                var current = start\n                while (current < end) {\n                    val emoji = model.getEmoji(current)\n                    if (emoji != null) {\n                        var shouldBreak = false\n                        if (model.removeEmoji(emoji)) {\n                            emojiModel = null\n                            shouldBreak = true\n                        }\n                        if (emoji.start < start) {\n                            pendingRemoveRange.add(emoji.start to start)\n                        }\n                        val emojiEnd = emoji.start + emoji.text.length\n                        if (emojiEnd > end) {\n                            val offset = count - after\n                            // remove first.\n                            pendingRemoveRange.add(0, (end - offset) to (emojiEnd - offset))\n                        }\n                        if (shouldBreak) {\n                            break;\n                        }\n                        current = emoji.start + emoji.text.length\n                    } else {\n                        current++\n                    }\n                }\n            }\n        }\n\n        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {\n            if (isTextManualSetting) {\n                return\n            }\n\n            val offset = count - before\n            emojiModel?.map?.let { map ->\n                map.valueIterator().asSequence().mapTo(correctingEmoji) { emoji ->\n                    if (emoji.start >= start) {\n                        emoji.start += offset\n                    }\n                    emoji\n                }\n                map.clear()\n                correctingEmoji.forEach {\n                    map.put(it.start, it)\n                }\n                correctingEmoji.clear()\n            }\n\n            if (isPendingRemoving) {\n                return\n            }\n\n            if (count > 0) {\n                val insertModel =\n                    textParser?.parse(s!!.subSequence(start, start + count))?.toEmojiModel(start){ emojiSize }\n                if (insertModel != null) {\n                    val model = emojiModel\n                    if (model != null) {\n                        model.merge(insertModel)\n                    } else {\n                        emojiModel = insertModel\n                    }\n                }\n            }\n\n        }\n\n        override fun afterTextChanged(s: Editable?) {\n            if (isPendingRemoving || isTextManualSetting) {\n                return\n            }\n            isPendingRemoving = true\n            pendingRemoveRange.forEach {\n                s?.delete(it.first, it.second)\n            }\n            pendingRemoveRange.clear()\n            s?.getSpans(0, s.length, EmojiSpan::class.java)?.forEach {\n                s.removeSpan(it)\n            }\n            emojiModel?.map?.valueIterator()?.forEach {\n                s?.setSpan(\n                    it.span,\n                    it.start,\n                    it.start + it.text.length,\n                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE\n                )\n            }\n\n            fixSelection()\n            isPendingRemoving = false\n        }\n\n    }\n\n    init {\n        super.addTextChangedListener(textWatcher)\n    }\n\n    fun replaceSelection(toInsert: CharSequence) {\n        val origin = text\n        if (origin == null) {\n            setText(toInsert)\n        } else {\n            if (selectionStart < 0 || selectionEnd < 0) {\n                setSelection(origin.length, origin.length)\n            }\n            if (selectionStart == selectionEnd) {\n                origin.insert(selectionEnd, toInsert)\n            } else {\n                var fixStart = selectionStart\n                emojiModel?.getEmoji(selectionStart)?.let {\n                    val end = it.start + it.text.length\n                    if (selectionStart > it.start && selectionStart == end - 1) {\n                        fixStart = end\n                    }\n                }\n                origin.replace(fixStart, selectionEnd, toInsert)\n            }\n        }\n    }\n\n    fun delete() {\n        val origin = text ?: return\n        if (selectionStart != selectionEnd) {\n            origin.replace(selectionStart, selectionEnd, \"\")\n        } else if (selectionStart > 0) {\n            origin.delete(selectionStart - 1, selectionEnd)\n        }\n    }\n\n    override fun setText(text: CharSequence?, type: BufferType?) {\n        isTextFirstSet = true\n        val model = textParser?.parse(text)?.toEmojiModel(0) { emojiSize }\n        emojiModel = model\n        val spannable = if (text is Spannable) {\n            val spans = text.getSpans(0, text.length, EmojiSpan::class.java)\n            for (span in spans) {\n                text.removeSpan(span)\n            }\n            text\n        } else {\n            SpannableString(text ?: \"\")\n        }\n        model?.map?.valueIterator()?.forEach {\n            spannable.setSpan(\n                it.span,\n                it.start,\n                it.start + it.text.length,\n                Spannable.SPAN_INCLUSIVE_EXCLUSIVE\n            )\n        }\n        isTextManualSetting = true\n        super.setText(spannable, type)\n        fixSelection()\n        isTextManualSetting = false\n    }\n\n    private fun fixSelection() {\n        val model = emojiModel ?: return\n        if (selectionStart == selectionEnd) {\n            val emoji = model.getEmoji(selectionStart) ?: return\n            if (selectionStart > emoji.start && selectionStart < (emoji.start + emoji.text.length)) {\n                setSelection(emoji.start + emoji.text.length)\n            }\n        } else {\n            var fixStart = selectionStart\n            var fixEnd = selectionEnd\n            val start = model.getEmoji(selectionStart)\n            if (start != null && selectionStart > start.start &&\n                selectionStart < (start.start + start.text.length)\n            ) {\n                fixStart = start.start\n            }\n            val end = model.getEmoji(selectionEnd)\n            if (end != null && selectionEnd > end.start && selectionEnd < (end.start + end.text.length)) {\n                fixEnd = end.start + end.text.length\n            }\n            setSelection(fixStart, fixEnd)\n        }\n\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/view/LineTypeView.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making QMUI_Android available.\n *\n * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.\n *\n * Licensed under the MIT License (the \"License\"); you may not use this file except in\n * compliance with the License. You may obtain a copy of the License at\n *\n * http://opensource.org/licenses/MIT\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is\n * distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.qmuiteam.qmui.type.view\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Typeface\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.util.Log\nimport android.view.MotionEvent\nimport android.view.accessibility.AccessibilityNodeInfo\nimport androidx.annotation.ColorInt\nimport com.qmuiteam.qmui.type.LineLayout\nimport com.qmuiteam.qmui.type.TypeEnvironment\nimport com.qmuiteam.qmui.type.TypeModel\nimport com.qmuiteam.qmui.type.parser.PlainTextParser\nimport com.qmuiteam.qmui.type.parser.TextParser\nimport java.util.*\n\nprivate const val TAG = \"LineTypeView\"\n\nopen class LineTypeView : BaseTypeView {\n\n    val lineLayout = LineLayout()\n\n    var textParser: TextParser = PlainTextParser.instance\n        set(value) {\n            if (field != value) {\n                field = value\n                lineLayout.typeModel = value.parse(text)\n                requestLayout()\n            }\n        }\n\n    private val touchSpanList = arrayListOf<TouchSpan>()\n    private var currentTouchSpan: TouchSpan? = null\n\n    constructor(context: Context) : super(context)\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val widthSize = MeasureSpec.getSize(widthMeasureSpec)\n        val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n        val heightSize = MeasureSpec.getSize(heightMeasureSpec)\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        environment.setMeasureLimit(widthSize - paddingLeft - paddingRight, heightSize - paddingTop - paddingBottom)\n        lineLayout.measureAndLayout(environment, heightMode == MeasureSpec.EXACTLY)\n        val usedWidth = if (widthMode == MeasureSpec.AT_MOST) {\n            lineLayout.maxLayoutWidth + paddingLeft + paddingRight\n        } else widthSize\n        val usedHeight = if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {\n            lineLayout.contentHeight + paddingTop + paddingBottom\n        } else heightSize\n        setMeasuredDimension(usedWidth, usedHeight)\n    }\n\n    var text: CharSequence? = null\n        set(value) {\n            if (field != value) {\n                field = value\n                touchSpanList.clear()\n                currentTouchSpan = null\n                lineLayout.typeModel = textParser.parse(value)\n                requestLayout()\n            }\n        }\n\n    var ellipsized: TextUtils.TruncateAt?\n        get() = lineLayout.ellipsize\n        set(value) {\n            if (lineLayout.ellipsize != value) {\n                lineLayout.ellipsize = value\n                requestLayout()\n            }\n        }\n\n    var maxLines: Int\n        get() = lineLayout.maxLines\n        set(value) {\n            if (lineLayout.maxLines != value) {\n                lineLayout.maxLines = value\n                requestLayout()\n            }\n        }\n\n    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {\n        super.onInitializeAccessibilityNodeInfo(info)\n        info.text = text\n        info.contentDescription = text\n    }\n\n    fun addClickEffect(\n        start: Int, end: Int,\n        textColorGetter: (isPressed: Boolean) -> Int,\n        bgColorGetter: (isPressed: Boolean) -> Int,\n        onClick: (start: Int, end: Int) -> Unit\n    ): TypeModel.EffectRemover? {\n        val types: MutableList<Int> = ArrayList()\n        types.add(TypeEnvironment.TYPE_BG_COLOR)\n        types.add(TypeEnvironment.TYPE_TEXT_COLOR)\n        return unsafeAddClickEffect(start, end, types, { env, touchSpan ->\n            env.textColor = textColorGetter.invoke(touchSpan.isPressed)\n            env.backgroundColor = bgColorGetter.invoke(touchSpan.isPressed)\n        }, onClick)\n    }\n\n    fun unsafeAddClickEffect(\n        start: Int, end: Int,\n        types: List<Int>, updater: (TypeEnvironment, touchSpan: TouchSpan) -> Unit, onClick: (Int, Int) -> Unit\n    ): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val touchSpan = TouchSpan(start, end, onClick)\n        val remover = typeModel.unsafeAddEffect(start, end, types) {\n            updater.invoke(it, touchSpan)\n        }\n        touchSpanList.add(touchSpan)\n        return TypeModel.EffectRemover {\n            remover?.remove()\n            touchSpanList.remove(touchSpan)\n        }\n    }\n\n    fun addBgEffect(start: Int, end: Int, @ColorInt color: Int): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val remover = typeModel.addBgEffect(start, end, color) ?: return null\n        invalidate()\n        return remover\n    }\n\n    fun addTextColorEffect(start: Int, end: Int, @ColorInt color: Int): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val remover = typeModel.addTextColorEffect(start, end, color) ?: return null\n        invalidate()\n        return remover\n    }\n\n    fun addUnderLineEffect(start: Int, end: Int, @ColorInt color: Int, height: Int): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val remover = typeModel.addUnderLineEffect(start, end, color, height) ?: return null\n        invalidate()\n        return remover\n    }\n\n    fun addTypefaceEffect(start: Int, end: Int, typeface: Typeface): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val remover = typeModel.addTypefaceEffect(start, end, typeface) ?: return null\n        requestLayout()\n        return remover\n    }\n\n    fun addTextSizeEffect(start: Int, end: Int, textSize: Float): TypeModel.EffectRemover? {\n        val typeModel = lineLayout.typeModel ?: return null\n        val remover = typeModel.addTextSizeEffect(start, end, textSize) ?: return null\n        requestLayout()\n        return remover\n    }\n\n    override fun onDraw(canvas: Canvas) {\n        canvas.save()\n        canvas.translate(paddingLeft.toFloat(), paddingTop.toFloat())\n        lineLayout.draw(canvas, environment)\n        canvas.restore()\n    }\n\n    override fun onTouchEvent(event: MotionEvent): Boolean {\n        val typeModel = lineLayout.typeModel ?: return super.onTouchEvent(event)\n        if (touchSpanList.isEmpty()) {\n            return super.onTouchEvent(event)\n        }\n        when (event.action) {\n            MotionEvent.ACTION_DOWN -> {\n                val current = currentTouchSpan\n                if (current != null) {\n                    Log.i(TAG, \"the currentTouchSpan is not null when touch down.\")\n                    current.isPressed = false\n                }\n                val touchSpan = findCurrentTouchSpan(typeModel, event.x, event.y)\n                if (touchSpan != null) {\n                    touchSpan.isPressed = true\n                    currentTouchSpan = touchSpan\n                    invalidate()\n                    return true\n                }\n            }\n            MotionEvent.ACTION_MOVE -> {\n                val current = currentTouchSpan\n                if (current != null) {\n                    if (!isSpanTouched(typeModel, current, event.x, event.y)) {\n                        current.isPressed = false\n                        val touchSpan = findCurrentTouchSpan(typeModel, event.x, event.y)\n                        if (touchSpan != null) {\n                            touchSpan.isPressed = true\n                            currentTouchSpan = touchSpan\n                        } else {\n                            currentTouchSpan = null\n                        }\n                        invalidate()\n                    }\n                    return true\n                } else {\n                    val touchSpan = findCurrentTouchSpan(typeModel, event.x, event.y)\n                    if (touchSpan != null) {\n                        touchSpan.isPressed = true\n                        currentTouchSpan = touchSpan\n                        invalidate()\n                        return true\n                    }\n                }\n            }\n            MotionEvent.ACTION_UP,\n            MotionEvent.ACTION_CANCEL -> {\n                val current = currentTouchSpan\n                if (current != null) {\n                    currentTouchSpan = null\n                    current.isPressed = false\n                    if (event.action == MotionEvent.ACTION_UP) {\n                        current.onClick.invoke(current.start, current.end)\n                    }\n                    invalidate()\n                    return true\n                }\n            }\n        }\n        return super.onTouchEvent(event)\n    }\n\n    private fun findCurrentTouchSpan(typeModel: TypeModel, x: Float, y: Float): TouchSpan? {\n        for (i in 0 until touchSpanList.size) {\n            val touchSpan = touchSpanList[i]\n            if (isSpanTouched(typeModel, touchSpan, x, y)) {\n                return touchSpan\n            }\n        }\n        return null\n    }\n\n    private fun isSpanTouched(typeModel: TypeModel, touchSpan: TouchSpan, x: Float, y: Float): Boolean {\n        val start = typeModel.getByPos(touchSpan.start) ?: return false\n        val end = typeModel.getByPos(touchSpan.end) ?: return false\n        if (start.y + paddingTop > y || end.y + paddingTop + end.measureHeight < y) {\n            return false\n        } else if (start.y == end.y) { // in one line\n            return !(start.x + paddingLeft > x || end.x + paddingLeft < x)\n        } else {\n            // in muti line\n            if (x < start.x + paddingLeft && y < start.y + start.measureHeight + paddingTop) {\n                return false\n            } else if (x > end.x + end.measureWidth + paddingLeft && y > end.y) {\n                return false\n            }\n            return true\n        }\n    }\n\n    class TouchSpan(val start: Int, val end: Int, val onClick: (Int, Int) -> Unit) {\n        var isPressed: Boolean = false\n            internal set\n    }\n}"
  },
  {
    "path": "type/src/main/java/com/qmuiteam/qmui/type/view/MarqueeTypeView.kt",
    "content": "package com.qmuiteam.qmui.type.view\n\nimport android.content.Context\nimport android.graphics.*\nimport android.os.SystemClock\nimport android.util.AttributeSet\nimport android.view.accessibility.AccessibilityNodeInfo\nimport com.qmuiteam.qmui.type.TypeModel\nimport com.qmuiteam.qmui.type.parser.PlainTextParser\nimport com.qmuiteam.qmui.type.parser.TextParser\n\nclass MarqueeTypeView : BaseTypeView {\n\n    constructor(context: Context): super(context)\n    constructor(context: Context, attrs: AttributeSet?): super(context, attrs)\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)\n\n    var typeModel: TypeModel? = null\n    var textParser: TextParser = PlainTextParser.instance\n        set(value) {\n            if (field != value) {\n                field = value\n                reset()\n                contentWidth = -1\n                typeModel = value.parse(text)\n                requestLayout()\n                if (isAttachedToWindow) {\n                    start()\n                }\n            }\n        }\n\n    var text: CharSequence? = null\n        set(value) {\n            if (field != value) {\n                field = value\n                reset()\n                contentWidth = -1\n                typeModel = textParser.parse(value)\n                requestLayout()\n                if (isAttachedToWindow) {\n                    start()\n                }\n            }\n        }\n\n    var keepTime: Long = 2000\n    var gap: Float = resources.displayMetrics.density * 50\n    var moveSpeedPerMs: Float = resources.displayMetrics.density / 36\n    var lastDrawTime = -2L\n\n    private var elementMaxHeight = 0\n    private var contentWidth = -1\n    private var fadeHelper: FadeHelper? = null\n    private var startX: Float = 0f\n\n\n    var fadeWidth: Float\n        get() = fadeHelper?.fadeWidth ?: 0f\n        set(value) {\n            (fadeHelper ?: FadeHelper().also { fadeHelper = it }).fadeWidth = value\n            invalidate()\n        }\n\n    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {\n        super.onInitializeAccessibilityNodeInfo(info)\n        info.text = text\n        info.contentDescription = text\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val widthSize = MeasureSpec.getSize(widthMeasureSpec)\n        val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n        val heightSize = MeasureSpec.getSize(heightMeasureSpec)\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        environment.setMeasureLimit(widthSize, heightSize)\n        measureAndLayoutModel()\n        val usedWidth = if (widthMode == MeasureSpec.AT_MOST) {\n            contentWidth.toInt()\n        } else widthSize\n        val usedHeight = if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {\n            elementMaxHeight.toInt()\n        } else heightSize\n\n        var current = typeModel?.firstElement()\n        while (current != null) {\n            current.y = (usedHeight - current.measureHeight) / 2\n            current = current.next\n        }\n        setMeasuredDimension(usedWidth, usedHeight)\n    }\n\n\n    private fun measureAndLayoutModel() {\n        elementMaxHeight = 0\n        var current = typeModel?.firstElement()\n        var x = 0\n        while (current != null) {\n            current.measure(environment)\n            current.x = x\n            elementMaxHeight = elementMaxHeight.coerceAtLeast(current.measureHeight)\n            x += current.measureWidth\n            current = current.next\n        }\n        contentWidth = x\n    }\n\n    override fun onAttachedToWindow() {\n        super.onAttachedToWindow()\n        if (!text.isNullOrBlank()) {\n            start()\n        }\n    }\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n        stop()\n    }\n\n    fun start() {\n        lastDrawTime = -1\n        invalidate()\n    }\n\n    fun stop() {\n        lastDrawTime = -2\n    }\n\n    fun reset() {\n        startX = 0f\n    }\n\n    override fun onDraw(canvas: Canvas) {\n        if(fadeHelper == null){\n            drawContent(canvas)\n        }else{\n            fadeHelper!!.drawFade(canvas){\n                drawContent(canvas)\n            }\n        }\n    }\n\n\n    private fun drawContent(canvas: Canvas){\n        if (contentWidth >0 && contentWidth <= width) {\n            lastDrawTime = -2\n            startX = 0f\n        }\n        canvas.save()\n        canvas.translate(startX, 0f)\n        var current = typeModel?.firstElement()\n        while (current != null) {\n            if (current.x + startX > width) {\n                break\n            } else if (current.x + startX + current.measureWidth > 0) {\n                current.draw(environment, canvas)\n            }\n            current = current.next\n        }\n        canvas.restore()\n        val gapRight = startX + contentWidth + gap\n        if (startX < 0 && gapRight < width) {\n            canvas.save()\n            canvas.translate(gapRight, 0f)\n            current = typeModel?.firstElement()\n            while (current != null) {\n                if (current.x + gapRight > width) {\n                    break\n                } else {\n                    current.draw(environment, canvas)\n                }\n                current = current.next\n            }\n            canvas.restore()\n        }\n        if (lastDrawTime == -2L) {\n            return\n        }\n        if (lastDrawTime == -1L) {\n            lastDrawTime = SystemClock.elapsedRealtime() + if(startX == 0f) keepTime else 0\n        } else {\n            val newTime = SystemClock.elapsedRealtime()\n            if(lastDrawTime < newTime){\n                startX -= moveSpeedPerMs * (newTime - lastDrawTime)\n                if(startX >= 0f && startX < resources.displayMetrics.density * 1.5){\n                    // allow 1.5dp error\n                    startX = 0f\n                    lastDrawTime = newTime + keepTime\n                }else{\n                    lastDrawTime = newTime\n                }\n\n                if (startX + contentWidth < 0) {\n                    startX += contentWidth + gap\n                }\n                // if drop many many frames. this condition can be matched, so recover to normal state.\n                if (startX + contentWidth < 0) {\n                    startX = 0f\n                }\n            } // else is keep time\n        }\n        postInvalidateOnAnimation()\n    }\n\n    private inner class FadeHelper{\n\n        var fadeWidth = 0f\n\n        private val paint = Paint().apply {\n            style = Paint.Style.FILL\n            xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)\n        }\n\n        private val leftFadeShader by lazy(LazyThreadSafetyMode.NONE) {\n            LinearGradient(\n                    0f, 0f, fadeWidth, 0f,\n                    intArrayOf(Color.TRANSPARENT, Color.BLACK), null, Shader.TileMode.CLAMP\n            )\n        }\n\n        private val rightFadeShader by lazy(LazyThreadSafetyMode.NONE) {\n            LinearGradient(\n                    0f, 0f, fadeWidth, 0f,\n                    intArrayOf(Color.BLACK, Color.TRANSPARENT), null, Shader.TileMode.CLAMP\n            )\n        }\n\n        inline fun drawFade(canvas: Canvas, action: (Canvas) -> Unit) = canvas.apply {\n            if(fadeWidth <= 0 || contentWidth <= width){\n                action(this)\n            }else{\n                val layerId = saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)\n                action(this)\n                if(startX < 0){\n                    paint.shader = leftFadeShader\n                    drawRect(0f, 0f, fadeWidth, height.toFloat(), paint)\n                }\n\n                translate((width - fadeWidth).coerceAtLeast(0f), 0f)\n                paint.shader = rightFadeShader\n                drawRect(0f, 0f, fadeWidth, height.toFloat(), paint)\n                restoreToCount(layerId)\n            }\n        }\n    }\n}"
  }
]